What is the difference between http::Method::try_from_bytes and from_str for parsing custom HTTP methods?
http::Method::try_from_bytes accepts byte slices (&[u8]) and handles both standard and extension/custom HTTP methods without allocating when possible, while from_str accepts string slices (&str) and only handles the standard methodsāattempting to parse a custom method with from_str returns an error. The key distinction is that try_from_bytes enables parsing arbitrary HTTP method names (like WEBDAV methods or custom extension methods) without requiring a string conversion first, making it suitable for parsing raw HTTP requests.
Standard HTTP Methods
use http::Method;
use std::str::FromStr;
fn standard_methods() {
// Both methods handle standard HTTP methods identically
// from_str for standard methods
let get: Method = Method::from_str("GET").unwrap();
let post: Method = Method::from_str("POST").unwrap();
let put: Method = Method::from_str("PUT").unwrap();
let delete: Method = Method::from_str("DELETE").unwrap();
// try_from_bytes for standard methods
let get_bytes = Method::try_from_bytes(b"GET").unwrap();
let post_bytes = Method::try_from_bytes(b"POST").unwrap();
// Both produce identical Method values for standard methods
assert_eq!(get, get_bytes);
assert_eq!(post, post_bytes);
// Standard methods are predefined constants
assert_eq!(Method::GET, Method::from_str("GET").unwrap());
assert_eq!(Method::POST, Method::try_from_bytes(b"POST").unwrap());
}Both methods successfully parse the standard HTTP methods (GET, POST, PUT, DELETE, HEAD, OPTIONS, TRACE, CONNECT, PATCH).
Extension Methods: The Key Difference
use http::Method;
use std::str::FromStr;
fn extension_methods() {
// Custom/extension HTTP methods (WebDAV, proprietary, etc.)
// try_from_bytes accepts ANY valid HTTP method name
let webdav = Method::try_from_bytes(b"PROPFIND").unwrap();
let report = Method::try_from_bytes(b"REPORT").unwrap();
let custom = Method::try_from_bytes(b"X-CUSTOM-METHOD").unwrap();
// These are valid Method values
println!("WebDAV method: {:?}", webdav); // Method::from_bytes(b"PROPFIND")
println!("Custom method: {:?}", custom); // Method::from_bytes(b"X-CUSTOM-METHOD")
// from_str REJECTS extension methods
let result = Method::from_str("PROPFIND");
println!("from_str result: {:?}", result); // Err(InvalidMethod)
// from_str only accepts the 9 standard methods
let result2 = Method::from_str("X-CUSTOM");
println!("Custom via from_str: {:?}", result2); // Err(InvalidMethod)
}try_from_bytes allows extension methods; from_str only accepts the predefined standard methods.
Method Validation Rules
use http::Method;
fn method_validation() {
// HTTP method naming rules (RFC 7231):
// - 1 to 255 characters
// - Only visible ASCII characters (no control chars, no spaces)
// - Case-sensitive (conventionally uppercase)
// Valid methods
assert!(Method::try_from_bytes(b"GET").is_ok());
assert!(Method::try_from_bytes(b"PROPFIND").is_ok()); // WebDAV
assert!(Method::try_from_bytes(b"X-MY-METHOD").is_ok()); // Custom
assert!(Method::try_from_bytes(b"M").is_ok()); // Single char
assert!(Method::try_from_bytes(b"MGET").is_ok()); // Extension
// Invalid methods
// Empty
assert!(Method::try_from_bytes(b"").is_err());
// Contains space
assert!(Method::try_from_bytes(b"GET POST").is_err());
// Contains control character
assert!(Method::try_from_bytes(b"GET\x00").is_err());
// From_str only accepts standard methods
assert!(Method::from_str("GET").is_ok()); // Standard
assert!(Method::from_str("WEBDAV").is_err()); // Extension - rejected
}try_from_bytes validates according to HTTP specs; from_str further restricts to known methods.
Working with Raw HTTP Data
use http::Method;
fn raw_http_data() {
// When parsing HTTP requests, you receive bytes
let request_line = b"PROPFIND /collection HTTP/1.1\r\n";
// Extract method bytes
let method_bytes = &request_line[..8]; // b"PROPFIND"
// try_from_bytes works directly
let method = Method::try_from_bytes(method_bytes).unwrap();
println!("Method: {}", method); // PROPFIND
// from_str would require conversion AND would fail for extensions
let method_str = std::str::from_utf8(method_bytes).unwrap();
let result = Method::from_str(method_str);
println!("from_str result: {:?}", result); // Err(InvalidMethod)
// Even if you convert to string, extension methods fail:
let webdav_str = "PROPFIND";
let result = Method::from_str(webdav_str);
assert!(result.is_err()); // Extension methods not recognized
}try_from_bytes is designed for raw HTTP data; from_str is limited to known methods.
Method Representation
use http::Method;
fn method_representation() {
// Standard methods use shared static instances
let get1 = Method::GET;
let get2 = Method::from_str("GET").unwrap();
let get3 = Method::try_from_bytes(b"GET").unwrap();
// All three reference the same static constant
assert!(std::ptr::eq(&get1, &get2));
assert!(std::ptr::eq(&get2, &get3));
// Extension methods allocate or use short-string optimization
let custom = Method::try_from_bytes(b"CUSTOM").unwrap();
let custom2 = Method::try_from_bytes(b"CUSTOM").unwrap();
// Extension methods are compared by value
assert_eq!(custom, custom2); // Equal values
// But may not be same pointer
// Standard methods are always cheap to create (no allocation)
// Extension methods may allocate for the name storage
}Standard methods use static constants; extension methods store their name.
Return Types and Errors
use http::Method;
use http::method::InvalidMethod;
use std::str::FromStr;
fn return_types() {
// try_from_bytes returns Result<Method, InvalidMethod>
let result: Result<Method, InvalidMethod> = Method::try_from_bytes(b"GET");
// from_str returns Result<Method, InvalidMethod>
// but ONLY succeeds for standard methods
let result1: Result<Method, InvalidMethod> = Method::from_str("GET");
// Error type is the same
match Method::try_from_bytes(b"INVALID METHOD") {
Ok(method) => println!("Method: {}", method),
Err(e) => println!("Error: {}", e), // "invalid HTTP method"
}
match Method::from_str("CUSTOM") {
Ok(method) => println!("Method: {}", method),
Err(e) => println!("Error: {}", e), // "invalid HTTP method"
}
// Both use InvalidMethod error type
// But for extension methods:
// - try_from_bytes: Succeeds
// - from_str: Returns InvalidMethod error
}Both return Result<Method, InvalidMethod>, but from_str fails for extension methods.
FromStr Trait Implementation
use http::Method;
use std::str::FromStr;
fn fromstr_trait() {
// FromStr is implemented for Method
// But it only accepts standard methods
// This works because FromStr is implemented
let method: Result<Method, _> = "GET".parse();
assert!(method.is_ok());
// Parsing a custom method fails
let custom: Result<Method, _> = "WEBDAV".parse();
assert!(custom.is_err());
// The FromStr impl is restrictive by design
// It enforces that string parsing only produces known methods
// For extension methods, you MUST use try_from_bytes
let bytes_method = Method::try_from_bytes(b"WEBDAV").unwrap();
}FromStr for Method is intentionally limited to standard methods.
Case Sensitivity
use http::Method;
fn case_sensitivity() {
// HTTP methods are case-sensitive
// Convention is uppercase
// Standard methods - case matters
assert!(Method::try_from_bytes(b"GET").is_ok()); // Correct
assert!(Method::try_from_bytes(b"get").is_ok()); // Different method!
// "get" is treated as extension method, not standard GET
let lowercase_get = Method::try_from_bytes(b"get").unwrap();
let uppercase_get = Method::try_from_bytes(b"GET").unwrap();
// These are NOT equal
assert_ne!(lowercase_get, uppercase_get);
// from_str is also case-sensitive
let from_str_get = Method::from_str("GET").unwrap();
let from_str_lower = Method::from_str("get");
assert!(from_str_lower.is_err()); // "get" is not recognized as GET
// Best practice: always use uppercase for HTTP methods
}Method names are case-sensitive; lowercase variants are treated as different (extension) methods.
Memory Efficiency
use http::Method;
use std::mem::size_of_val;
fn memory_efficiency() {
// Method is designed to be memory-efficient
// Standard methods are interned (single static instance)
let get = Method::GET;
// Extension methods store the name inline or allocate
// Small methods may use inline storage (short-string optimization)
let short = Method::try_from_bytes(b"WEBDAV").unwrap();
// Long method names may allocate
let long = Method::try_from_bytes(b"X-VERY-LONG-CUSTOM-METHOD-NAME").unwrap();
// Size is consistent (enum)
println!("Size of Method: {} bytes", std::mem::size_of::<Method>());
// try_from_bytes may allocate for extension methods
// from_str never allocates (returns error for extensions)
// For standard methods, both are zero-allocation
// Method::GET is just a reference to a static constant
}Standard methods are zero-allocation; extension methods may allocate for name storage.
Integration with HTTP Libraries
use http::{Method, Request, Response};
fn http_integration() {
// When building requests programmatically
// Use Method constants for standard methods
let request = Request::builder()
.method(Method::GET) // Use constant directly
.uri("/api/users")
.body(())
.unwrap();
// For WebDAV or custom methods, use try_from_bytes
let webdav = Method::try_from_bytes(b"PROPFIND").unwrap();
let request = Request::builder()
.method(webdav)
.uri("/collection")
.body(())
.unwrap();
// Parsing incoming HTTP requests
fn parse_request(method_bytes: &[u8], uri: &str) -> Option<Request<()>> {
// Use try_from_bytes for raw request parsing
let method = Method::try_from_bytes(method_bytes).ok()?;
Request::builder()
.method(method)
.uri(uri)
.body(())
.ok()
}
}Use try_from_bytes when parsing incoming HTTP requests; use constants for standard methods.
Common Extension Methods
use http::Method;
fn common_extensions() {
// WebDAV methods (RFC 4918)
let propfind = Method::try_from_bytes(b"PROPFIND").unwrap();
let proppatch = Method::try_from_bytes(b"PROPPATCH").unwrap();
let mkcol = Method::try_from_bytes(b"MKCOL").unwrap();
let copy = Method::try_from_bytes(b"COPY").unwrap();
let move_method = Method::try_from_bytes(b"MOVE").unwrap();
let lock = Method::try_from_bytes(b"LOCK").unwrap();
let unlock = Method::try_from_bytes(b"UNLOCK").unwrap();
// Other common extensions
let report = Method::try_from_bytes(b"REPORT").unwrap();
let merge = Method::try_from_bytes(b"MERGE").unwrap();
let label = Method::try_from_bytes(b"LABEL").unwrap();
// None of these work with from_str
assert!(Method::from_str("PROPFIND").is_err());
assert!(Method::from_str("LOCK").is_err());
// They all require try_from_bytes
}WebDAV and other extension methods require try_from_bytes.
Display and Debug
use http::Method;
fn display_debug() {
// Both standard and extension methods display correctly
let get = Method::GET;
let custom = Method::try_from_bytes(b"CUSTOM").unwrap();
// Display shows the method name
println!("Standard: {}", get); // "GET"
println!("Extension: {}", custom); // "CUSTOM"
// Debug also shows the method
println!("Debug standard: {:?}", get); // Method::GET
println!("Debug extension: {:?}", custom); // Method::from_bytes(b"CUSTOM")
// as_str returns &str for all methods
println!("as_str: {}", get.as_str()); // "GET"
println!("as_str: {}", custom.as_str()); // "CUSTOM"
// Both types work identically for display purposes
}Both standard and extension methods have consistent Display and as_str behavior.
Type Signatures
use http::Method;
use http::method::InvalidMethod;
fn type_signatures() {
// try_from_bytes signature:
// pub fn try_from_bytes(src: &[u8]) -> Result<Method, InvalidMethod>
// Takes &[u8] (byte slice)
// Returns Result<Method, InvalidMethod>
// Accepts ANY valid HTTP method name
// FromStr::from_str signature (via trait):
// fn from_str(s: &str) -> Result<Self, Self::Err>
// where Err = InvalidMethod
// Takes &str (string slice)
// Returns Result<Method, InvalidMethod>
// ONLY accepts standard HTTP methods
// Use try_from_bytes when:
// - Parsing raw HTTP requests (bytes)
// - Supporting extension methods
// - Working with WebDAV or custom protocols
// Use from_str when:
// - Parsing user input that should only be standard methods
// - Enforcing method restrictions
// - Configuration file parsing
}The type signatures reflect their different purposes: bytes vs string, flexible vs restrictive.
Method Equality and Comparison
use http::Method;
fn method_equality() {
// Standard methods compare efficiently
let get1 = Method::GET;
let get2 = Method::try_from_bytes(b"GET").unwrap();
let get3 = Method::from_str("GET").unwrap();
// All equal
assert_eq!(get1, get2);
assert_eq!(get2, get3);
// Extension methods compare by content
let custom1 = Method::try_from_bytes(b"CUSTOM").unwrap();
let custom2 = Method::try_from_bytes(b"CUSTOM").unwrap();
let other = Method::try_from_bytes(b"OTHER").unwrap();
assert_eq!(custom1, custom2); // Same name
assert_ne!(custom1, other); // Different name
// Method implements PartialEq, Eq, Hash
// Can use as HashMap key
let mut handlers = std::collections::HashMap::new();
handlers.insert(Method::GET, "handle_get");
handlers.insert(Method::try_from_bytes(b"WEBDAV").unwrap(), "handle_webdav");
// Clone is cheap for standard methods, may allocate for extensions
let cloned_get = get1.clone(); // Zero cost (Copy for standard)
let cloned_custom = custom1.clone(); // May allocate
}Method implements equality, hashing, and cloning appropriately for both standard and extension methods.
Best Practices
use http::Method;
fn best_practices() {
// For known standard methods:
// - Use Method constants directly (Method::GET, Method::POST)
// - Most efficient, no parsing
// For parsing HTTP requests:
// - Use try_from_bytes
// - Accepts all valid methods
// - Works with raw bytes from network
// For user input that should be restricted:
// - Use from_str or .parse()
// - Rejects extension methods
// - Clear error for invalid input
// For WebDAV or custom protocols:
// - Use try_from_bytes
// - Extension methods are first-class citizens
// Example: Request parsing
fn parse_method(raw_bytes: &[u8]) -> Method {
Method::try_from_bytes(raw_bytes)
.expect("Invalid HTTP method in request")
}
// Example: Configuration parsing
fn parse_config_method(s: &str) -> Result<Method, String> {
Method::from_str(s)
.map_err(|e| format!("Invalid method '{}': {}", s, e))
}
}Choose based on context: constants for standard methods, try_from_bytes for protocol parsing, from_str for restricted input.
Synthesis
Quick comparison:
| Aspect | try_from_bytes |
from_str |
|---|---|---|
| Input type | &[u8] (bytes) |
&str (string) |
| Standard methods | ā Works | ā Works |
| Extension methods | ā Works | ā Fails |
| Use case | HTTP protocol parsing | Config/user input |
| Allocation | May allocate for extensions | Never allocates |
Decision guide:
// Use Method constants directly:
let method = Method::GET;
let method = Method::POST;
// Use try_from_bytes for:
// - Raw HTTP request parsing
// - WebDAV methods (PROPFIND, LOCK, etc.)
// - Custom extension methods
// - Byte-oriented APIs
let method = Method::try_from_bytes(b"PROPFIND").unwrap();
// Use from_str for:
// - User configuration
// - CLI argument parsing
// - Enforcing standard methods only
let method = Method::from_str("GET").unwrap();
let method: Method = "POST".parse().unwrap();Key insight: The distinction exists because HTTP/1.1 defines only 8 standard methods (GET, POST, PUT, DELETE, HEAD, OPTIONS, CONNECT, TRACE, plus PATCH in later specs), but the protocol allows ANY valid token as a method name. Extension methods are legitimateāWebDAV defines PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK, and custom protocols may define their own. from_str is intentionally restrictive to catch typos in configuration files where "GOT" instead of "GET" should be an error. try_from_bytes is lenient because an HTTP server must accept any valid method from a client, even if it will later return 405 Method Not Allowed. The byte slice input (&[u8]) matches how HTTP data arrives from the networkāyou shouldn't need to convert to UTF-8 just to parse a method name when the bytes are already valid.
