Loading page…
Rust walkthroughs
Loading page…
http::Request builder pattern differ from directly constructing request structs?The http::Request builder pattern provides a fluent API for incrementally constructing requests, handling defaults and validation while allowing partial construction. Direct struct construction requires specifying all fields upfront with no validation. The builder pattern is more ergonomic for typical use cases where you set headers, method, and URI separately, while direct construction gives you complete control and is useful when you already have all components ready.
use http::{Request, Method, Uri, Version, header::HeaderMap};
fn direct_construction() {
// Direct construction requires all parts at once
let mut headers = HeaderMap::new();
headers.insert("Content-Type", "application/json".parse().unwrap());
headers.insert("Authorization", "Bearer token".parse().unwrap());
let request = Request {
method: Method::POST,
uri: "/api/users".parse::<Uri>().unwrap(),
version: Version::HTTP_11,
headers: headers,
body: Some(r#"{"name":"Alice"}"#.to_string()),
};
// Access parts through the request
println!("Method: {}", request.method());
println!("URI: {}", request.uri());
}Direct construction requires creating all components explicitly.
use http::Request;
fn builder_construction() {
// Builder allows incremental construction
let request = Request::builder()
.method("POST")
.uri("/api/users")
.header("Content-Type", "application/json")
.header("Authorization", "Bearer token")
.body(r#"{"name":"Alice"}"#.to_string())
.unwrap();
println!("Method: {}", request.method());
println!("URI: {}", request.uri());
}The builder chains method calls for a more readable construction.
use http::{Request, Method, Uri};
fn method_and_uri() {
// Builder: fluent method calls
let request = Request::builder()
.method(Method::GET)
.uri(Uri::from_static("/users/123"))
.body(())
.unwrap();
// Builder also accepts string conversion
let request = Request::builder()
.method("PUT")
.uri("/users/123")
.body(())
.unwrap();
// Direct: must construct Method and Uri explicitly
let request = Request {
method: Method::PUT,
uri: "/users/123".parse().unwrap(),
version: http::Version::HTTP_11,
headers: Default::default(),
body: (),
};
}The builder handles string parsing and conversion automatically.
use http::{Request, header::HeaderMap, header::HeaderValue};
fn headers_comparison() {
// Builder: append headers fluently
let request = Request::builder()
.header("Content-Type", "application/json")
.header("X-Custom-Header", "value")
.header("Accept", "application/json")
.body(())
.unwrap();
// Direct: construct HeaderMap explicitly
let mut headers = HeaderMap::new();
headers.insert("Content-Type", HeaderValue::from_static("application/json"));
headers.insert("X-Custom-Header", HeaderValue::from_static("value"));
headers.insert("Accept", HeaderValue::from_static("application/json"));
let request = Request {
method: http::Method::GET,
uri: "/".parse().unwrap(),
version: http::Version::HTTP_11,
headers,
body: (),
};
}The builder's header() method handles parsing and insertion.
use http::{Request, Version};
fn version_handling() {
// Builder: optional version (defaults to HTTP/1.1)
let request = Request::builder()
.uri("/")
.body(())
.unwrap();
// Uses default Version::HTTP_11
let request = Request::builder()
.uri("/")
.version(Version::HTTP_2)
.body(())
.unwrap();
// Direct: must specify version
let request = Request {
method: http::Method::GET,
uri: "/".parse().unwrap(),
version: Version::HTTP_2, // Explicit
headers: Default::default(),
body: (),
};
}The builder provides sensible defaults for version.
use http::Request;
fn error_handling() {
// Builder returns Result on body()
let result = Request::builder()
.method("INVALID METHOD") // Invalid method
.body(());
// Returns Err because method is invalid
// Check for errors
match result {
Ok(request) => println!("Built: {:?}", request.method()),
Err(e) => println!("Error building request: {}", e),
}
// Builder validation happens at body() time
let result = Request::builder()
.uri("not a valid uri") // Invalid URI
.body(());
// Returns Err
// Direct construction: parsing errors happen immediately
let uri_result: Result<http::Uri, _> = "invalid".parse();
// You handle parse errors before constructing the request
}The builder collects validation errors and returns them at finalization.
use http::Request;
fn conditional_headers(auth_token: Option<&str>) {
// Builder: conditionally add headers
let mut builder = Request::builder()
.method("GET")
.uri("/api/data")
.header("Accept", "application/json");
if let Some(token) = auth_token {
builder = builder.header("Authorization", format!("Bearer {}", token));
}
let request = builder.body(()).unwrap();
// This pattern is common for optional headers
}The builder can be stored and extended conditionally.
use http::Request;
fn extensions() {
// Builder: add extensions fluently
let request = Request::builder()
.uri("/")
.extension(MyExtension { id: 42 })
.body(())
.unwrap();
// Access extension
let ext: &MyExtension = request.extensions().get().unwrap();
println!("Extension ID: {}", ext.id);
// Direct: access extensions mutably
let mut request = Request::new(());
request.extensions_mut().insert(MyExtension { id: 42 });
}
#[derive(Debug)]
struct MyExtension {
id: u32,
}Extensions store arbitrary data alongside the request.
use http::Request;
fn body_types() {
// String body
let request = Request::builder()
.uri("/api/users")
.body(r#"{"name":"Alice"}"#.to_string())
.unwrap();
// Bytes body
let request = Request::builder()
.uri("/api/binary")
.body(vec![0, 1, 2, 3, 4])
.unwrap();
// Unit body for GET requests
let request = Request::builder()
.uri("/api/users")
.body(())
.unwrap();
// Streaming body (hyper)
// let request = Request::builder()
// .uri("/upload")
// .body(hyper::Body::wrap_stream(...))
// .unwrap();
}The builder is generic over the body type.
use http::Request;
fn partial_construction() {
// Start building, then extend based on conditions
let builder = Request::builder()
.uri("/api/resource");
// Different methods based on operation
let builder = match determine_operation() {
Operation::Read => builder.method("GET"),
Operation::Write => builder.method("POST").header("Content-Type", "application/json"),
Operation::Delete => builder.method("DELETE"),
};
// Add auth if available
let builder = add_auth_if_present(builder);
// Finalize
let request = builder.body(()).unwrap();
}
enum Operation {
Read,
Write,
Delete,
}
fn determine_operation() -> Operation {
Operation::Read
}
fn add_auth_if_present(builder: http::request::Builder) -> http::request::Builder {
// Conditionally add auth header
builder.header("X-Requested-With", "XMLHttpRequest")
}Builders can be passed around and extended incrementally.
use http::{Request, Method, Uri, header::HeaderName, header::HeaderValue};
fn type_safety() {
// Builder: automatic string parsing with error at body()
let request = Request::builder()
.method("GET") // Parsed to Method
.uri("/path") // Parsed to Uri
.header("Content-Type", "text/plain") // Parsed to HeaderName/Value
.body(())
.unwrap();
// Direct: explicit types, errors at parse time
let method: Method = "GET".parse().unwrap();
let uri: Uri = "/path".parse().unwrap();
let mut headers = http::HeaderMap::new();
let name: HeaderName = "Content-Type".parse().unwrap();
let value: HeaderValue = "text/plain".parse().unwrap();
headers.insert(name, value);
let request = Request {
method,
uri,
version: http::Version::HTTP_11,
headers,
body: (),
};
}The builder defers parsing errors; direct construction surfaces them immediately.
use http::Request;
struct RequestTemplate {
base_url: String,
default_headers: Vec<(String, String)>,
}
impl RequestTemplate {
fn create_request(&self, path: &str) -> http::request::Builder {
let mut builder = Request::builder()
.uri(format!("{}{}", self.base_url, path));
for (name, value) in &self.default_headers {
builder = builder.header(name, value);
}
builder
}
}
fn use_template() {
let template = RequestTemplate {
base_url: "https://api.example.com".to_string(),
default_headers: vec![
("Accept".to_string(), "application/json".to_string()),
("User-Agent".to_string(), "MyApp/1.0".to_string()),
],
};
// Create request from template
let request = template.create_request("/users")
.method("GET")
.body(())
.unwrap();
}Returning builders enables reusable request templates.
use http::{Request, Method, Uri, Version, header::HeaderMap};
fn complete_control() {
// When you need exact control over all components
let headers = HeaderMap::from_iter([
("Content-Type".parse().unwrap(), "application/json".parse().unwrap()),
("Content-Length".parse().unwrap(), "42".parse().unwrap()),
]);
let request = Request {
method: Method::POST,
uri: Uri::from_static("/api/endpoint"),
version: Version::HTTP_11,
headers, // Fully controlled HeaderMap
body: Some(vec![0u8; 42]), // Specific body
};
// Useful when:
// - Deserializing from another format
// - Copying components from another request
// - Constructing from pre-parsed parts
}Direct construction is useful when you have pre-validated components.
use http::Request;
fn request_parts() {
let request = Request::builder()
.method("POST")
.uri("/api/users")
.header("Content-Type", "application/json")
.body(r#"{"name":"Alice"}"#.to_string())
.unwrap();
// Both builder and direct construction result in the same type
// Access is the same regardless of construction method
println!("Method: {}", request.method());
println!("URI: {}", request.uri());
println!("Version: {:?}", request.version());
println!("Headers: {:?}", request.headers());
println!("Body: {:?}", request.body());
// Deconstruct into parts
let (parts, body) = request.into_parts();
println!("Parts method: {}", parts.method);
println!("Body: {}", body);
}Both construction methods produce the same Request<T> type.
use http::Request;
fn performance_comparison() {
// Builder has slight overhead from:
// - Option wrapping of each component
// - Validation at finalization
// - Potential intermediate allocations
// Direct construction:
// - No validation overhead
// - Explicit control over allocations
// - All components must be ready
// For hot paths with known-valid inputs:
// Direct construction may be marginally faster
// For typical use:
// Builder is preferred for readability and safety
}The builder adds minimal overhead for typical use cases.
use http::{Request, Method, Uri, Version, header::HeaderMap};
fn when_to_use_builder() {
// Use builder when:
// - Constructing requests from parameters
// - Conditionally adding headers
// - Want readable, chainable syntax
// - Want validation with Result handling
let request = Request::builder()
.method("POST")
.uri("/api/users")
.header("Content-Type", "application/json")
.header("Authorization", "Bearer token")
.body(create_user_json())
.unwrap();
}
fn when_to_use_direct() {
// Use direct construction when:
// - All components are pre-validated
// - Copying from another request
// - Building from parsed/deserialized data
// - Need exact control over every field
let headers = parse_headers_from_raw(raw_headers);
let request = Request {
method: parsed_method,
uri: parsed_uri,
version: Version::HTTP_11,
headers,
body: parsed_body,
};
}
fn create_user_json() -> String {
r#"{"name":"Alice"}"#.to_string()
}
fn parse_headers_from_raw(raw: &str) -> HeaderMap {
HeaderMap::new()
}
static raw_headers: &str = "";
static parsed_method: Method = Method::GET;
static parsed_uri: Uri = Uri::from_static("/");
static parsed_body: Option<String> = None;Choose based on where your data comes from and how much validation you need.
The builder pattern and direct construction serve different purposes:
Builder advantages:
Direct construction advantages:
Use builder when:
Use direct construction when: