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.