Loading page…
Rust walkthroughs
Loading page…
http::StatusCode::as_u16 vs StatusCode::as_str for HTTP status handling?HTTP status codes serve as the primary mechanism for conveying request outcomes in HTTP responses. The http::StatusCode type provides two distinct representations: as_u16 returns the numeric code (like 200 or 404), while as_str returns the canonical reason phrase (like "OK" or "Not Found"). These methods serve different purposes: numeric codes are essential for programmatic handling, logging, and interoperation with systems that expect numbers, while reason phrases provide human-readable context for debugging, error messages, and HTTP/1.1 response formatting. Understanding when to use each representation helps build systems that are both machine-processable and human-understandable.
use http::StatusCode;
fn basic_usage() {
let status = StatusCode::OK;
// Numeric representation
let code: u16 = status.as_u16();
assert_eq!(code, 200);
// String representation (reason phrase)
let reason: &str = status.as_str();
assert_eq!(reason, "OK");
// Display shows "200 OK"
println!("{}", status); // "200 OK"
// Debug shows more detail
println!("{:?}", status); // "OK"
}The two methods provide different views of the same status code.
use http::StatusCode;
fn numeric_operations() {
let status = StatusCode::NOT_FOUND;
// Use numeric code for:
// 1. Programmatic handling
let code = status.as_u16();
if code >= 400 && code < 500 {
println!("Client error: {}", code);
}
// 2. Classification
fn classify_status(status: StatusCode) -> &'static str {
match status.as_u16() {
100..=199 => "informational",
200..=299 => "success",
300..=399 => "redirection",
400..=499 => "client error",
500..=599 => "server error",
_ => "unknown",
}
}
// 3. Logging numeric values
struct LogEntry {
status_code: u16,
path: String,
}
let entry = LogEntry {
status_code: status.as_u16(),
path: "/api/users".to_string(),
};
// 4. Metrics collection
fn record_metric(status: StatusCode) {
let code = status.as_u16();
// Metrics systems often expect numeric values
// HISTOGRAM("http.status", code);
}
}Numeric codes enable programmatic classification and data processing.
use http::StatusCode;
fn string_operations() {
let status = StatusCode::INTERNAL_SERVER_ERROR;
// Use reason phrase for:
// 1. Error messages
fn error_message(status: StatusCode) -> String {
format!("Request failed: {} ({})", status.as_str(), status.as_u16())
}
println!("{}", error_message(status));
// "Request failed: Internal Server Error (500)"
// 2. HTTP/1.1 response formatting
fn format_response_line(status: StatusCode) -> String {
format!("HTTP/1.1 {} {}", status.as_u16(), status.as_str())
}
let line = format_response_line(StatusCode::CREATED);
assert_eq!(line, "HTTP/1.1 201 Created");
// 3. User-facing messages
fn user_message(status: StatusCode) -> &'static str {
status.as_str()
}
}Reason phrases provide human context for status codes.
use http::StatusCode;
fn reason_phrases() {
// Standard status codes have defined reason phrases
let statuses = vec![
(StatusCode::OK, "OK"),
(StatusCode::CREATED, "Created"),
(StatusCode::NO_CONTENT, "No Content"),
(StatusCode::MOVED_PERMANENTLY, "Moved Permanently"),
(StatusCode::FOUND, "Found"),
(StatusCode::BAD_REQUEST, "Bad Request"),
(StatusCode::UNAUTHORIZED, "Unauthorized"),
(StatusCode::FORBIDDEN, "Forbidden"),
(StatusCode::NOT_FOUND, "Not Found"),
(StatusCode::INTERNAL_SERVER_ERROR, "Internal Server Error"),
(StatusCode::BAD_GATEWAY, "Bad Gateway"),
(StatusCode::SERVICE_UNAVAILABLE, "Service Unavailable"),
];
for (status, expected) in statuses {
assert_eq!(status.as_str(), expected);
}
// Custom status codes also work
let custom = StatusCode::from_u16(299).unwrap();
// Unknown codes return empty string for as_str
println!("Custom status: '{}' is empty for unknown", custom.as_str());
}Well-known status codes have canonical reason phrases; unknown codes return empty strings.
use http::StatusCode;
fn custom_status_codes() {
// Create status from u16
let custom = StatusCode::from_u16(299).unwrap();
// Numeric representation always works
assert_eq!(custom.as_u16(), 299);
// String representation is empty for unknown codes
assert_eq!(custom.as_str(), "");
// Common patterns for custom codes
fn is_custom_status(status: StatusCode) -> bool {
// Non-standard status codes
!matches!(status.as_u16(),
100..=199 | // Informational
200..=299 | // Success
300..=399 | // Redirection
400..=499 | // Client Error
500..=599 // Server Error
)
}
// WebDAv extensions (200-level)
let webdav_207 = StatusCode::from_u16(207).unwrap(); // Multi-Status
let webdav_208 = StatusCode::from_u16(208).unwrap(); // Already Reported
// Unofficial codes
let unofficial_103 = StatusCode::from_u16(103).unwrap(); // Early Hints
// as_str may have predefined values for some extensions
println!("207: '{}'", webdav_207.as_str()); // "Multi-Status"
}StatusCode handles both standard and custom numeric codes gracefully.
use http::StatusCode;
fn display_debug() {
let status = StatusCode::NOT_FOUND;
// Display shows "404 Not Found"
let display = format!("{}", status);
assert_eq!(display, "404 Not Found");
// Debug shows reason phrase
let debug = format!("{:?}", status);
assert_eq!(debug, "NOT_FOUND");
// Both representations include different info
// Display: full HTTP status line component
// Debug: constant name
// For comparison
let created = StatusCode::CREATED;
println!("Display: {}", created); // "201 Created"
println!("Debug: {:?}", created); // "CREATED"
}Display includes both code and reason; Debug shows the constant name.
use http::StatusCode;
fn classification_helpers() {
// StatusCode provides helper methods for classification
let status = StatusCode::OK;
// Check status classes
assert!(status.is_success());
assert!(!status.is_client_error());
assert!(!status.is_server_error());
// Class-based patterns
fn handle_status(status: StatusCode) {
if status.is_informational() {
// 1xx: Continue with request
} else if status.is_success() {
// 2xx: Request succeeded
} else if status.is_redirection() {
// 3xx: Follow redirect
} else if status.is_client_error() {
// 4xx: Fix client request
println!("Client error: {}", status.as_u16());
} else if status.is_server_error() {
// 5xx: Server issue
println!("Server error: {}", status);
}
}
// These methods use as_u16 internally
// is_success: 200-299
// is_client_error: 400-499
// is_server_error: 500-599
}Helper methods like is_success() are built on numeric ranges.
use http::{StatusCode, Response, Version};
use http::header;
fn response_formatting() {
// Building HTTP/1.1 response line
fn build_status_line(version: Version, status: StatusCode) -> String {
match version {
Version::HTTP_09 => format!("{}", status.as_u16()), // No reason in HTTP/0.9
Version::HTTP_10 => format!("HTTP/1.0 {} {}", status.as_u16(), status.as_str()),
Version::HTTP_11 => format!("HTTP/1.1 {} {}", status.as_u16(), status.as_str()),
Version::HTTP_2 => format!(":status: {}", status.as_u16()), // HTTP/2 uses pseudo-headers
_ => format!("HTTP/1.1 {} {}", status.as_u16(), status.as_str()),
}
}
let status_line = build_status_line(Version::HTTP_11, StatusCode::OK);
assert_eq!(status_line, "HTTP/1.1 200 OK");
// HTTP/2 doesn't use reason phrases
// Only the numeric code in pseudo-header
let status = StatusCode::NOT_FOUND;
// HTTP/2: :status: 404
println!("HTTP/2 status: {}", status.as_u16());
}HTTP versions differ in whether they include reason phrases.
use http::StatusCode;
use std::collections::HashMap;
fn logging_metrics() {
// Numeric codes for metrics systems
fn record_status_metric(status: StatusCode) {
let code = status.as_u16();
// Prometheus-style metrics
let metric = format!("http_requests_total{{status=\"{}\"}}", code);
println!("{}", metric);
// Or histogram buckets
// HTTP_STATUS_BUCKET.observe(code as f64);
}
// Structured logging
#[derive(serde::Serialize)]
struct LogRecord {
status_code: u16,
status_reason: &'static str,
timestamp: u64,
}
fn log_response(status: StatusCode) {
let record = LogRecord {
status_code: status.as_u16(),
status_reason: status.as_str(),
timestamp: 1234567890,
};
println!("{}", serde_json::to_string(&record).unwrap());
}
// Aggregation by numeric code
let mut status_counts: HashMap<u16, u32> = HashMap::new();
for status in [StatusCode::OK, StatusCode::OK, StatusCode::NOT_FOUND] {
*status_counts.entry(status.as_u16()).or_insert(0) += 1;
}
assert_eq!(status_counts.get(&200), Some(&2));
assert_eq!(status_counts.get(&404), Some(&1));
}Numeric codes are essential for metrics aggregation; reason phrases aid human interpretation.
use http::StatusCode;
fn error_handling() {
// Matching on numeric values
fn handle_response(status: StatusCode) -> Result<String, String> {
match status.as_u16() {
200 => Ok("Success".to_string()),
201 => Ok("Created".to_string()),
204 => Ok("No Content".to_string()),
400 => Err(format!("Bad Request: {}", status.as_str())),
401 => Err(format!("Unauthorized: {}", status.as_str())),
403 => Err(format!("Forbidden: {}", status.as_str())),
404 => Err(format!("Not Found: {}", status.as_str())),
500..=599 => Err(format!("Server Error {}: {}", status.as_u16(), status.as_str())),
_ => Err(format!("Unknown status: {}", status.as_u16())),
}
}
// Using built-in classification
fn classify_error(status: StatusCode) -> &'static str {
if status.is_client_error() {
"Fix your request"
} else if status.is_server_error() {
"Try again later"
} else if status.is_success() {
"All good"
} else {
"Unexpected"
}
}
// Detailed error with both representations
fn detailed_error(status: StatusCode, message: &str) -> String {
format!(
"HTTP {}: {} - {}",
status.as_u16(),
status.as_str(),
message
)
}
let error = detailed_error(StatusCode::BAD_GATEWAY, "Upstream timeout");
assert_eq!(error, "HTTP 502: Bad Gateway - Upstream timeout");
}Combining numeric and string representations provides comprehensive error information.
use http::StatusCode;
fn status_constants() {
// StatusCode provides constants for common codes
let ok = StatusCode::OK; // 200
let created = StatusCode::CREATED; // 201
let accepted = StatusCode::ACCEPTED; // 202
let no_content = StatusCode::NO_CONTENT; // 204
let moved = StatusCode::MOVED_PERMANENTLY; // 301
let found = StatusCode::FOUND; // 302
let see_other = StatusCode::SEE_OTHER; // 303
let not_modified = StatusCode::NOT_MODIFIED; // 304
let bad_request = StatusCode::BAD_REQUEST; // 400
let unauthorized = StatusCode::UNAUTHORIZED; // 401
let forbidden = StatusCode::FORBIDDEN; // 403
let not_found = StatusCode::StatusCode::NOT_FOUND; // 404
let internal = StatusCode::INTERNAL_SERVER_ERROR; // 500
let bad_gateway = StatusCode::BAD_GATEWAY; // 502
let unavailable = StatusCode::SERVICE_UNAVAILABLE; // 503
// These constants avoid typos and provide type safety
// Compare:
// let status = 200; // Could be any number
// let status = StatusCode::OK; // Clearly a status code
}Type-safe constants prevent typos and make intent clear.
use http::StatusCode;
fn conversions() {
// From u16
let status = StatusCode::from_u16(200).unwrap();
assert_eq!(status, StatusCode::OK);
// From u16 can fail for invalid codes
let invalid = StatusCode::from_u16(99);
assert!(invalid.is_err());
// Valid range: 100-999
assert!(StatusCode::from_u16(99).is_err());
assert!(StatusCode::from_u16(100).is_ok());
assert!(StatusCode::from_u16(999).is_ok());
assert!(StatusCode::from_u16(1000).is_err());
// To u16 via as_u16
let code: u16 = StatusCode::OK.as_u16();
// Parse from string
let from_str: StatusCode = "200".parse().unwrap();
assert_eq!(from_str, StatusCode::OK);
// Reason phrase strings also work for known codes
let from_reason: StatusCode = "OK".parse().unwrap();
assert_eq!(from_reason, StatusCode::OK);
}StatusCode supports parsing from strings and conversion from u16 with validation.
use http::StatusCode;
fn http2_considerations() {
// HTTP/2 doesn't transmit reason phrases
// Only the numeric status code is sent
fn build_http2_headers(status: StatusCode) -> Vec<(&'static str, String)> {
// HTTP/2 uses :status pseudo-header with numeric code
vec![
(":status", status.as_u16().to_string()),
// Reason phrase is NOT included in HTTP/2
]
}
let headers = build_http2_headers(StatusCode::OK);
assert_eq!(headers[0], (":status", "200".to_string()));
// HTTP/1.1 includes reason phrase
fn build_http1_status_line(status: StatusCode) -> String {
format!("HTTP/1.1 {} {}", status.as_u16(), status.as_str())
}
let line = build_http1_status_line(StatusCode::OK);
assert_eq!(line, "HTTP/1.1 200 OK");
}HTTP/2 uses only numeric codes; reason phrases are HTTP/1.1-specific.
use http::StatusCode;
fn defaults() {
// Default status code is 200 OK
let default: StatusCode = Default::default();
assert_eq!(default, StatusCode::OK);
// Common default patterns
fn success_response() -> StatusCode {
StatusCode::OK // Most common success
}
fn created_response() -> StatusCode {
StatusCode::CREATED // Resource created
}
fn no_content_response() -> StatusCode {
StatusCode::NO_CONTENT // Success with no body
}
fn bad_request(message: &str) -> (StatusCode, String) {
(StatusCode::BAD_REQUEST, message.to_string())
}
fn not_found(resource: &str) -> (StatusCode, String) {
(StatusCode::NOT_FOUND, format!("{} not found", resource))
}
fn internal_error() -> StatusCode {
StatusCode::INTERNAL_SERVER_ERROR
}
}StatusCode::OK is the default, reflecting the most common response.
use http::StatusCode;
use http::header;
struct HttpResponse {
status: StatusCode,
headers: Vec<(&'static str, String)>,
body: Option<String>,
}
impl HttpResponse {
fn new(status: StatusCode) -> Self {
HttpResponse {
status,
headers: Vec::new(),
body: None,
}
}
fn with_body(status: StatusCode, body: String) -> Self {
HttpResponse {
status,
headers: vec![("Content-Type", "application/json".to_string())],
body: Some(body),
}
}
fn to_http1_1(&self) -> String {
let mut response = format!(
"HTTP/1.1 {} {}\r\n",
self.status.as_u16(),
self.status.as_str()
);
for (name, value) in &self.headers {
response.push_str(&format!("{}: {}\r\n", name, value));
}
if let Some(body) = &self.body {
response.push_str(&format!("Content-Length: {}\r\n", body.len()));
response.push_str("\r\n");
response.push_str(body);
} else {
response.push_str("\r\n");
}
response
}
}
fn handle_request(path: &str) -> HttpResponse {
match path {
"/health" => HttpResponse::new(StatusCode::OK),
"/api/users" => HttpResponse::with_body(
StatusCode::OK,
r#"[{"id": 1, "name": "Alice"}]"#.to_string()
),
"/api/users/1" => HttpResponse::with_body(
StatusCode::OK,
r#"{"id": 1, "name": "Alice"}"#.to_string()
),
_ => HttpResponse::with_body(
StatusCode::NOT_FOUND,
r#"{"error": "Not found"}"#.to_string()
),
}
}Web frameworks use both numeric and string representations when formatting responses.
use http::StatusCode;
struct AccessLog {
method: String,
path: String,
status_code: u16,
status_reason: &'static str,
duration_ms: u64,
}
impl AccessLog {
fn to_json(&self) -> String {
format!(
r#"{{"method":"{}","path":"{}","status":{},"reason":"{}","duration":{}}}"#,
self.method, self.path, self.status_code, self.status_reason, self.duration_ms
)
}
fn to_common_log_format(&self) -> String {
// Common Log Format: host ident authuser date request status bytes
format!(
"- - - [timestamp] \"{} {}\" {} -",
self.method, self.path, self.status_code
)
}
}
fn logging_middleware(status: StatusCode, method: &str, path: &str, duration_ms: u64) {
let log = AccessLog {
method: method.to_string(),
path: path.to_string(),
status_code: status.as_u16(),
status_reason: status.as_str(),
duration_ms,
};
// Structured logging for log aggregation
println!("{}", log.to_json());
// Human-readable for console
println!("[{}] {} {} - {} ({})",
log.duration_ms, log.method, log.path, log.status_code, log.status_reason);
}
fn example_logging() {
logging_middleware(StatusCode::OK, "GET", "/api/users", 15);
// Output: [15] GET /api/users - 200 (OK)
logging_middleware(StatusCode::NOT_FOUND, "POST", "/api/unknown", 3);
// Output: [3] POST /api/unknown - 404 (Not Found)
}Logs benefit from both numeric codes (for parsing) and reason phrases (for human readers).
Method comparison:
| Method | Return Type | Use Case |
|--------|-------------|----------|
| as_u16() | u16 | Programmatic handling, metrics, HTTP/2 |
| as_str() | &'static str | Human-readable messages, HTTP/1.1, debugging |
When to use each:
| Scenario | Preferred Method | Reason |
|----------|-----------------|--------|
| Metrics collection | as_u16() | Numeric aggregation |
| Status classification | as_u16() | Range-based matching |
| HTTP/2 responses | as_u16() | HTTP/2 doesn't use reason phrases |
| Error messages | as_str() | Human context |
| HTTP/1.1 response line | Both | "200 OK" format |
| Log files | Both | Machine parseable + readable |
Status code ranges:
| Range | Class | Example Codes | |-------|-------|----------------| | 100-199 | Informational | 100 Continue, 101 Switching Protocols | | 200-299 | Success | 200 OK, 201 Created, 204 No Content | | 300-399 | Redirection | 301 Moved Permanently, 304 Not Modified | | 400-499 | Client Error | 400 Bad Request, 404 Not Found | | 500-599 | Server Error | 500 Internal Server Error, 503 Unavailable |
Key insight: The as_u16() and as_str() methods serve complementary purposes in HTTP status handling. The numeric representation (as_u16()) is essential for programmatic processing—classifying responses, aggregating metrics, building HTTP/2 headers, and routing error handling logic. The string representation (as_str()) provides the canonical reason phrase defined in HTTP specifications, adding human-readable context for error messages, log files, and HTTP/1.1 response formatting. The http::StatusCode type gives you both representations with type safety, ensuring you never accidentally use an invalid status code. For comprehensive error information, combine both: format!("HTTP {}: {}", status.as_u16(), status.as_str()) produces "HTTP 404: Not Found", providing both the machine-processable code and human context in a single message.