Loading pageâŚ
Rust walkthroughs
Loading pageâŚ
http::uri::Builder::scheme enable safe URI construction with validation?http::uri::Builder::scheme provides a type-safe, validated approach to constructing URIs by enforcing scheme correctness at compile time where possible and validating at runtime when necessary. The builder pattern ensures that invalid schemes cannot be set, preventing malformed URIs from being constructed. Each builder method validates its input and returns a Result or the builder itself, allowing for chained construction that maintains invariants throughout the building process. This approach catches errors earlyâduring construction rather than when the URI is usedâproviding clear error messages and preventing invalid URIs from propagating through the application.
use http::uri::{Builder, Scheme, Uri};
fn main() {
// Build a URI step by step
let uri: Uri = Builder::new()
.scheme("https")
.authority("example.com")
.path_and_query("/path?query=value")
.build()
.expect("Invalid URI");
println!("URI: {}", uri);
println!("Scheme: {}", uri.scheme().unwrap());
println!("Authority: {}", uri.authority().unwrap());
}The builder pattern allows constructing URIs incrementally with validation at each step.
use http::uri::Builder;
fn main() {
// Valid schemes
let valid = Builder::new()
.scheme("https")
.authority("example.com")
.path_and_query("/")
.build();
println!("Valid scheme: {:?}", valid.is_ok());
// Invalid scheme (contains invalid characters)
let invalid = Builder::new()
.scheme("http$")
.authority("example.com")
.path_and_query("/")
.build();
println!("Invalid scheme: {:?}", invalid);
// Scheme must start with letter
let invalid_start = Builder::new()
.scheme("123http")
.authority("example.com")
.path_and_query("/")
.build();
println!("Invalid start: {:?}", invalid_start);
}Schemes are validated according to RFC 3986: they must start with a letter and contain only letters, digits, +, -, or ..
use http::uri::{Builder, Scheme};
fn main() {
// Create a typed Scheme
let scheme: Scheme = "https".parse().unwrap();
// Use typed Scheme with builder
let uri = Builder::new()
.scheme(scheme.clone())
.authority("example.com")
.path_and_query("/api")
.build()
.unwrap();
println!("URI: {}", uri);
// Common schemes are predefined
let http_scheme = Scheme::HTTP;
let https_scheme = Scheme::HTTPS;
println!("HTTP scheme: {}", http_scheme);
println!("HTTPS scheme: {}", https_scheme);
}Scheme type provides compile-time safety for known schemes and validation for dynamic values.
use http::uri::Scheme;
fn main() {
// Predefined schemes
let http: Scheme = Scheme::HTTP;
let https: Scheme = Scheme::HTTPS;
println!("HTTP: {}", http);
println!("HTTPS: {}", https);
// These are constants, not strings
// No validation needed
let uri = http::uri::Builder::new()
.scheme(Scheme::HTTPS)
.authority("rust-lang.org")
.path_and_query("/")
.build()
.unwrap();
println!("URI: {}", uri);
}Scheme::HTTP and Scheme::HTTPS are predefined constants that bypass validation overhead.
use http::uri::Builder;
fn main() {
// Valid scheme characters
let examples = [
"http", // Lowercase letters
"HTTP", // Uppercase letters
"https", // Mixed use
"my-scheme", // With hyphen
"my.scheme", // With dot
"my+scheme", // With plus
"scheme123", // With digits (not at start)
"a", // Single letter
];
for scheme in examples {
let result = Builder::new()
.scheme(scheme)
.authority("example.com")
.path_and_query("/")
.build();
println!("{}: valid={}", scheme, result.is_ok());
}
// Invalid scheme examples
let invalid_examples = [
"123scheme", // Starts with digit
"scheme name", // Contains space
"scheme:name", // Contains colon
"scheme/", // Contains slash
"", // Empty
];
for scheme in invalid_examples {
let result = Builder::new()
.scheme(scheme)
.authority("example.com")
.path_and_query("/")
.build();
println!("{}: valid={}", scheme, result.is_ok());
}
}Schemes must follow RFC 3986: letter followed by letters, digits, +, -, or ..
use http::uri::Builder;
fn build_uri(scheme: &str, host: &str) -> Result<http::Uri, String> {
Builder::new()
.scheme(scheme)
.authority(host)
.path_and_query("/")
.build()
.map_err(|e| format!("URI build failed: {}", e))
}
fn main() {
match build_uri("https", "example.com") {
Ok(uri) => println!("Built: {}", uri),
Err(e) => println!("Error: {}", e),
}
match build_uri("invalid scheme", "example.com") {
Ok(uri) => println!("Built: {}", uri),
Err(e) => println!("Error: {}", e),
}
}Builder returns Result allowing proper error handling for invalid inputs.
use http::uri::{Builder, Scheme, Authority, PathAndQuery};
fn main() {
// Method 1: String components
let uri1 = Builder::new()
.scheme("https")
.authority("api.example.com:8080")
.path_and_query("/users/123?active=true")
.build()
.unwrap();
println!("URI 1: {}", uri1);
// Method 2: Typed components
let scheme: Scheme = "https".parse().unwrap();
let authority: Authority = "api.example.com:8080".parse().unwrap();
let path: PathAndQuery = "/users/123?active=true".parse().unwrap();
let uri2 = Builder::new()
.scheme(scheme)
.authority(authority)
.path_and_query(path)
.build()
.unwrap();
println!("URI 2: {}", uri2);
// Both produce identical URIs
assert_eq!(uri1, uri2);
}Components can be provided as strings (parsed and validated) or as typed values.
use http::uri::Builder;
fn main() {
// Start with empty builder
let mut builder = Builder::new();
// Add components conditionally
let use_https = true;
let has_port = true;
builder = if use_https {
builder.scheme("https")
} else {
builder.scheme("http")
};
builder = builder.authority(if has_port {
"example.com:8080"
} else {
"example.com"
});
builder = builder.path_and_query("/api/v1");
// Build at the end
let uri = builder.build().unwrap();
println!("URI: {}", uri);
}Builder supports incremental construction with conditional components.
use http::uri::Builder;
fn main() {
// Scheme-only (for relative references)
let scheme_only = Builder::new()
.scheme("https")
.build()
.unwrap();
println!("Scheme only: {}", scheme_only);
// Scheme and authority only
let no_path = Builder::new()
.scheme("https")
.authority("example.com")
.build()
.unwrap();
println!("No path: {}", no_path);
// All components
let complete = Builder::new()
.scheme("https")
.authority("example.com")
.path_and_query("/path")
.build()
.unwrap();
println!("Complete: {}", complete);
}The builder can create partial URIs with only some components.
use http::Uri;
fn main() {
// Start with existing URI
let original: Uri = "http://example.com/path".parse().unwrap();
// Create parts for modification
let mut builder = http::uri::Builder::new();
// Copy components, changing scheme
builder = builder
.scheme("https")
.authority(original.authority().unwrap().clone())
.path_and_query(original.path_and_query().unwrap().clone());
let modified = builder.build().unwrap();
println!("Original: {}", original);
println!("Modified: {}", modified);
}Existing URIs can be deconstructed and rebuilt with modifications.
use http::uri::{Builder, Scheme};
fn main() {
// Schemes are case-insensitive per RFC 3986
let uri1 = Builder::new()
.scheme("HTTP")
.authority("example.com")
.path_and_query("/")
.build()
.unwrap();
let uri2 = Builder::new()
.scheme("http")
.authority("example.com")
.path_and_query("/")
.build()
.unwrap();
// Both URIs are equivalent
println!("URI1: {}", uri1);
println!("URI2: {}", uri2);
// Scheme comparison is case-insensitive
let scheme1: Scheme = "HTTP".parse().unwrap();
let scheme2: Scheme = "http".parse().unwrap();
println!("Schemes equal: {}", scheme1 == scheme2);
}Schemes are normalized and compared case-insensitively.
use http::uri::Builder;
fn main() {
// Custom schemes are allowed if valid
let custom = Builder::new()
.scheme("myapp")
.authority("localhost")
.path_and_query("/resource")
.build()
.unwrap();
println!("Custom scheme: {}", custom);
// URI schemes for custom protocols
let schemes = [
"ftp", "ws", "wss", "file", "mailto", "tel",
"my-custom-app", "app+extension"
];
for scheme in schemes {
let uri = Builder::new()
.scheme(scheme)
.authority("host")
.path_and_query("/")
.build();
println!("{}: {:?}", scheme, uri.is_ok());
}
}Custom schemes work if they follow RFC 3986 character rules.
use http::{uri::Builder, Request, Method};
fn build_request(scheme: &str, host: &str, path: &str) -> Result<Request<String>, http::Error> {
let uri = Builder::new()
.scheme(scheme)
.authority(host)
.path_and_query(path)
.build()?;
Request::builder()
.method(Method::GET)
.uri(uri)
.body(String::new())
}
fn main() {
match build_request("https", "api.github.com", "/users/rust-lang") {
Ok(request) => {
println!("Method: {}", request.method());
println!("URI: {}", request.uri());
}
Err(e) => println!("Error: {}", e),
}
}Built URIs integrate directly with HTTP request construction.
use http::uri::Builder;
fn main() {
// Builder is consumed by build()
let builder = Builder::new()
.scheme("https")
.authority("example.com")
.path_and_query("/");
let uri1 = builder.build();
println!("First build: {:?}", uri1.is_ok());
// Cannot use builder after build()
// let uri2 = builder.build(); // Error: builder moved
// Create new builder for another URI
let uri2 = Builder::new()
.scheme("http")
.authority("example.com")
.path_and_query("/")
.build()
.unwrap();
println!("Second URI: {}", uri2);
}build() consumes the builder, enforcing single-use semantics.
use http::Uri;
fn main() {
let uri: Uri = http::uri::Builder::new()
.scheme("https")
.authority("user:pass@host.com:8080")
.path_and_query("/path?query=value")
.build()
.unwrap();
println!("Full URI: {}", uri);
// Access individual parts
if let Some(scheme) = uri.scheme() {
println!("Scheme: {}", scheme);
}
if let Some(authority) = uri.authority() {
println!("Authority: {}", authority);
println!(" Host: {}", authority.host());
if let Some(port) = authority.port() {
println!(" Port: {}", port);
}
}
if let Some(path) = uri.path_and_query() {
println!("Path: {}", path.path());
if let Some(query) = path.query() {
println!("Query: {}", query);
}
}
}The resulting URI provides typed access to all components.
use http::uri::Builder;
fn main() {
// The builder ensures invariants:
// 1. Valid scheme characters
let bad_chars = Builder::new()
.scheme("scheme with spaces")
.build();
assert!(bad_chars.is_err());
// 2. Valid authority format
let bad_auth = Builder::new()
.scheme("https")
.authority("not valid authority")
.build();
assert!(bad_auth.is_err());
// 3. Valid path format
let bad_path = Builder::new()
.scheme("https")
.authority("example.com")
.path_and_query("invalid path")
.build();
assert!(bad_path.is_err());
// Successful builds produce valid URIs
let valid = Builder::new()
.scheme("https")
.authority("example.com")
.path_and_query("/valid/path")
.build();
assert!(valid.is_ok());
println!("All validations passed");
}Each component is validated independently before building.
use http::{Uri, uri::Builder};
fn main() {
// Method 1: Parse from string
let parsed: Uri = "https://example.com/path?query=value"
.parse()
.unwrap();
println!("Parsed: {}", parsed);
// Method 2: Build from components
let built = Builder::new()
.scheme("https")
.authority("example.com")
.path_and_query("/path?query=value")
.build()
.unwrap();
println!("Built: {}", built);
// Both produce equivalent URIs
assert_eq!(parsed.to_string(), built.to_string());
// Builder advantages:
// 1. Type-safe components
// 2. Clear validation errors
// 3. Incremental construction
// 4. Conditional component setting
}Builder provides more control than parsing complete URI strings.
Scheme validation rules (RFC 3986):
| Rule | Description |
|------|-------------|
| First character | Must be a letter (A-Z, a-z) |
| Subsequent characters | Letters, digits, +, -, . |
| Case insensitive | HTTP = http = HtTp |
| No reserved characters | No :, /, ?, #, etc. |
Builder method types:
| Method | Input Type | Returns |
|--------|------------|---------|
| scheme(&str) | String slice | Builder |
| scheme(Scheme) | Typed scheme | Builder |
| build() | - | Result<Uri, Error> |
Predefined schemes:
| Constant | Value |
|----------|-------|
| Scheme::HTTP | "http" |
| Scheme::HTTPS | "https" |
Builder vs string parsing:
| Aspect | Builder | String Parsing | |--------|---------|----------------| | Type safety | Typed components | String only | | Error location | Per-component | Whole string | | Conditional building | Supported | Difficult | | Incremental | Supported | Not applicable |
Key insight: http::uri::Builder::scheme enables safe URI construction by validating each component independently before combining them into a complete URI. The builder pattern provides three key safety guarantees: (1) scheme validation ensures only valid RFC 3986 characters are accepted, preventing malformed URIs at construction time; (2) typed components (Scheme, Authority, PathAndQuery) can be pre-parsed and reused, avoiding redundant validation; (3) the build() method returns a Result, forcing explicit error handling rather than panicking on invalid input. This approach catches errors earlyâduring URI constructionârather than when the URI is used in network operations, providing clear error messages about which component failed validation. The builder also supports incremental and conditional construction, making it suitable for configuration-driven URI generation where components may vary based on runtime conditions.