Loading pageā¦
Rust walkthroughs
Loading pageā¦
http::HeaderMap handle multiple values for the same header name compared to a standard HashMap?http::HeaderMap is specifically designed for HTTP headers, which frequently require multiple values for the same header nameāsuch as multiple Set-Cookie headers or comma-separated Accept values. Unlike HashMap<K, V> which maps each unique key to exactly one value, HeaderMap stores multiple values per header name while maintaining insertion order. It also provides case-insensitive key lookup, which is essential for HTTP headers where Content-Type and content-type are semantically identical. The combination of multi-value support, insertion order preservation, and case-insensitive keys makes HeaderMap suitable for HTTP semantics where HashMap would be insufficient.
use http::HeaderMap;
use http::header::{CONTENT_TYPE, USER_AGENT};
fn main() {
let mut headers = HeaderMap::new();
// Insert single value
headers.insert(CONTENT_TYPE, "application/json".parse().unwrap());
// Append multiple values for same header
headers.append(USER_AGENT, "Mozilla/5.0".parse().unwrap());
headers.append(USER_AGENT, "MyApp/1.0".parse().unwrap());
// Get first value (like HashMap)
println!("Content-Type: {:?}", headers.get(CONTENT_TYPE));
// Get all values (HeaderMap specific)
println!("User-Agent values:");
for value in headers.get_all(USER_AGENT) {
println!(" {}", value.to_str().unwrap());
}
}HeaderMap supports both single-value access and iteration over all values.
use http::HeaderMap;
use http::header::SET_COOKIE;
fn main() {
let mut headers = HeaderMap::new();
// Multiple Set-Cookie headers are common
headers.append(SET_COOKIE, "session=abc123; Path=/".parse().unwrap());
headers.append(SET_COOKIE, "theme=dark; Path=/".parse().unwrap());
headers.append(SET_COOKIE, "lang=en; Path=/".parse().unwrap());
// HashMap would overwrite previous values
// use std::collections::HashMap;
// let mut map = HashMap::new();
// map.insert(SET_COOKIE, "session=abc123"); // First insert
// map.insert(SET_COOKIE, "theme=dark"); // Overwrites first!
// HeaderMap preserves all values
println!("All Set-Cookie values:");
for value in headers.get_all(SET_COOKIE) {
println!(" {}", value.to_str().unwrap());
}
// Output:
// All Set-Cookie values:
// session=abc123; Path=/
// theme=dark; Path=/
// lang=en; Path=/
}HeaderMap stores all values; HashMap overwrites previous values.
use http::HeaderMap;
use http::header::CONTENT_TYPE;
fn main() {
let mut headers = HeaderMap::new();
// insert() replaces all values
headers.insert(CONTENT_TYPE, "text/html".parse().unwrap());
headers.insert(CONTENT_TYPE, "application/json".parse().unwrap());
// Only last insert remains
println!("After inserts: {:?}", headers.get(CONTENT_TYPE));
// "application/json"
// append() adds to existing values
let mut headers2 = HeaderMap::new();
headers2.append(CONTENT_TYPE, "text/html".parse().unwrap());
headers2.append(CONTENT_TYPE, "application/json".parse().unwrap());
// Both values preserved
println!("After appends:");
for value in headers2.get_all(CONTENT_TYPE) {
println!(" {}", value.to_str().unwrap());
}
// text/html
// application/json
// insert returns old value if present
let mut headers3 = HeaderMap::new();
headers3.insert(CONTENT_TYPE, "text/plain".parse().unwrap());
let old = headers3.insert(CONTENT_TYPE, "text/html".parse().unwrap());
println!("Old value: {:?}", old); // Some("text/plain")
}insert replaces all values; append adds to existing values.
use http::HeaderMap;
use http::header::CONTENT_TYPE;
fn main() {
let mut headers = HeaderMap::new();
// Insert with standard name
headers.insert(CONTENT_TYPE, "application/json".parse().unwrap());
// Lookup with different case
println!("get(\"content-type\"): {:?}", headers.get("content-type"));
println!("get(\"Content-Type\"): {:?}", headers.get("Content-Type"));
println!("get(\"CONTENT-TYPE\"): {:?}", headers.get("CONTENT-TYPE"));
// All return the same value!
// HashMap would treat these as different keys
use std::collections::HashMap;
let mut map = HashMap::new();
map.insert("Content-Type", "application/json");
println!("HashMap get: {:?}", map.get("content-type")); // None
println!("HashMap get: {:?}", map.get("Content-Type")); // Some
// HeaderName type ensures case-insensitive comparison
use http::header::HeaderName;
let name1: HeaderName = "content-type".parse().unwrap();
let name2: HeaderName = "Content-Type".parse().unwrap();
assert_eq!(name1, name2); // Same header name!
}HeaderMap uses case-insensitive lookup; HashMap is case-sensitive.
use http::HeaderMap;
fn main() {
let mut headers = HeaderMap::new();
headers.insert("X-Custom-A", "value-a".parse().unwrap());
headers.insert("X-Custom-B", "value-b".parse().unwrap());
headers.insert("X-Custom-C", "value-c".parse().unwrap());
// HeaderMap preserves insertion order
println!("Headers in insertion order:");
for (name, value) in &headers {
println!(" {}: {}", name, value.to_str().unwrap());
}
// X-Custom-A, X-Custom-B, X-Custom-C
// HashMap does not preserve order (use IndexMap if you need ordered HashMap)
use std::collections::HashMap;
let mut map = HashMap::new();
map.insert("X-Custom-A", "value-a");
map.insert("X-Custom-B", "value-b");
map.insert("X-Custom-C", "value-c");
println!("\nHashMap iteration (arbitrary order):");
for (name, value) in &map {
println!(" {}: {}", name, value);
}
// Order is unpredictable
}HeaderMap preserves insertion order; HashMap order is undefined.
use http::HeaderMap;
fn main() {
let mut headers = HeaderMap::new();
headers.append("Accept", "text/html".parse().unwrap());
headers.append("Accept", "application/json".parse().unwrap());
headers.append("Accept", "*/*".parse().unwrap());
// get() returns first value only
println!("First Accept: {:?}", headers.get("Accept"));
// Some("text/html")
// get_all() returns iterator over all values
println!("All Accept values:");
for value in headers.get_all("Accept") {
println!(" {}", value.to_str().unwrap());
}
// text/html
// application/json
// */*
// len() counts header entries (not values)
println!("Header entries: {}", headers.len()); // 1 entry (one name)
// keys_len() counts unique header names
println!("Unique header names: {}", headers.keys_len()); // 1
}get returns the first value; get_all returns an iterator over all values.
use http::HeaderMap;
fn main() {
let mut headers = HeaderMap::new();
headers.insert("Content-Type", "application/json".parse().unwrap());
headers.append("Accept", "text/html".parse().unwrap());
headers.append("Accept", "application/json".parse().unwrap());
headers.insert("Server", "MyServer/1.0".parse().unwrap());
// Iterate over all header entries
// Each (name, value) pair yields once per value
println!("All header entries:");
for (name, value) in &headers {
println!(" {}: {}", name, value.to_str().unwrap());
}
// Content-Type: application/json
// Accept: text/html
// Accept: application/json
// Server: MyServer/1.0
// Iterate over keys only
println!("\nHeader names:");
for name in headers.keys() {
println!(" {}", name);
}
// Note: repeated names appear once in keys()
// Iterate over values only
println!("\nAll values:");
for value in headers.values() {
println!(" {}", value.to_str().unwrap());
}
}Iteration yields each value separately for multi-value headers.
use http::HeaderMap;
use std::collections::HashMap;
fn main() {
// Feature comparison:
// 1. Multiple values per key
// HeaderMap: YES - append() adds values
// HashMap: NO - insert() replaces
let mut hm = HeaderMap::new();
hm.append("X", "1".parse().unwrap());
hm.append("X", "2".parse().unwrap());
assert_eq!(hm.get_all("X").count(), 2);
let mut map = HashMap::new();
map.insert("X", "1");
map.insert("X", "2");
assert_eq!(map.get("X"), Some(&"2")); // Only last value
// 2. Case-insensitive keys
// HeaderMap: YES
// HashMap: NO
// 3. Insertion order
// HeaderMap: YES
// HashMap: NO (use IndexMap for ordered map)
// 4. Typed header values
// HeaderMap: HeaderValue (validates ASCII)
// HashMap: Any value type
// 5. HTTP-specific methods
// HeaderMap: get_all, append, contains_key
// HashMap: get, insert, contains_key
}HeaderMap is purpose-built for HTTP semantics; HashMap is general-purpose.
use http::{HeaderMap, HeaderValue};
use http::header::CONTENT_TYPE;
fn main() {
let mut headers = HeaderMap::new();
// HeaderValue must be valid ASCII
let valid = HeaderValue::from_static("application/json");
headers.insert(CONTENT_TYPE, valid);
// Invalid values rejected at parse time
let invalid = "application/json\nmalicious".parse::<HeaderValue>();
println!("Invalid header: {:?}", invalid);
// Err(InvalidHeaderValue)
// HeaderValue ensures:
// - ASCII only (no UTF-8)
// - No control characters (except space/tab in values)
// - Valid for HTTP transmission
// from_str for dynamic values
let dynamic = HeaderValue::from_str("text/plain; charset=utf-8").unwrap();
headers.insert(CONTENT_TYPE, dynamic);
// to_str() extracts string value
if let Some(value) = headers.get(CONTENT_TYPE) {
println!("Content-Type: {}", value.to_str().unwrap());
}
}HeaderValue enforces HTTP header validity; HashMap accepts any value type.
use http::HeaderMap;
fn main() {
let mut headers = HeaderMap::new();
headers.insert("X-Auth-Token", "secret123".parse().unwrap());
headers.insert("X-Request-Id", "abc-123".parse().unwrap());
// remove() removes all values for a header
let old_value = headers.remove("X-Auth-Token");
println!("Removed: {:?}", old_value);
// Some("secret123")
// After removal, header doesn't exist
println!("Still exists: {:?}", headers.get("X-Auth-Token"));
// None
// remove returns all values if multiple existed
let mut headers2 = HeaderMap::new();
headers2.append("Accept", "text/html".parse().unwrap());
headers2.append("Accept", "application/json".parse().unwrap());
// Note: remove() only returns the first value currently
// (This is a known limitation; get_all().remove() pattern for all)
}remove deletes all values for a header name.
use http::HeaderMap;
use http::header::{CONTENT_TYPE, SET_COOKIE, CACHE_CONTROL};
fn build_response_headers() -> HeaderMap {
let mut headers = HeaderMap::new();
// Single value headers
headers.insert(CONTENT_TYPE, "text/html; charset=utf-8".parse().unwrap());
headers.insert(CACHE_CONTROL, "max-age=3600".parse().unwrap());
// Multiple Set-Cookie headers (very common)
headers.append(SET_COOKIE, "session=abc123; HttpOnly; Secure".parse().unwrap());
headers.append(SET_COOKIE, "preferences=dark; Path=/settings".parse().unwrap());
headers.append(SET_COOKIE, "tracking=disabled; Max-Age=86400".parse().unwrap());
// Multiple Accept values (can also be comma-separated)
headers.append("Accept", "text/html".parse().unwrap());
headers.append("Accept", "application/xhtml+xml".parse().unwrap());
headers.append("Accept", "application/xml;q=0.9".parse().unwrap());
headers
}
fn main() {
let headers = build_response_headers();
// Process headers for HTTP response
println!("HTTP/1.1 200 OK");
for (name, value) in &headers {
println!("{}: {}", name, value.to_str().unwrap());
}
println!("\nSet-Cookie headers:");
for cookie in headers.get_all(SET_COOKIE) {
println!(" {}", cookie.to_str().unwrap());
}
// Count unique headers
println!("\nUnique header names: {}", headers.keys_len());
println!("Total header entries: {}", headers.len());
}HeaderMap naturally models real HTTP header semantics.
use http::HeaderMap;
use std::collections::HashMap;
fn main() {
// Converting HashMap to HeaderMap
let mut map = HashMap::new();
map.insert("Content-Type", "application/json");
map.insert("X-Custom", "value");
let mut headers = HeaderMap::new();
for (k, v) in map {
// Note: loses type safety, need to parse
let name = k.parse().unwrap();
let value = v.parse().unwrap();
headers.insert(name, value);
}
// Warning: HashMap loses multi-value semantics
// If you had multiple values in HashMap, you'd need HashMap<K, Vec<V>>
// Converting HeaderMap to HashMap (loses multi-values)
let converted: HashMap<_, _> = headers.iter()
.map(|(k, v)| (k.to_string(), v.to_str().unwrap().to_string()))
.collect();
// Note: This loses:
// 1. Additional values for same header
// 2. Case-insensitive semantics
// 3. Insertion order
}Conversion requires care for multi-value headers.
use http::HeaderMap;
use std::collections::HashMap;
fn main() {
// HeaderMap internal structure:
// - Uses a hash map for O(1) lookup
// - Maintains a list for ordered iteration
// - Stores multiple values per key
// HashMap<K, V>:
// - Single value per key
// - O(1) average lookup
// - No ordering guarantee
// Memory comparison:
// HeaderMap has more overhead for:
// - Case-insensitive key storage
// - Multiple values per key
// - Insertion order tracking
// Use HeaderMap when:
// - Multiple values per header
// - Case-insensitive lookup needed
// - Insertion order matters
// - HTTP header semantics
// Use HashMap when:
// - Single value per key
// - Case-sensitive keys
// - Order doesn't matter
// - General-purpose key-value storage
}HeaderMap has overhead for HTTP-specific features; HashMap is leaner but lacks them.
Core distinction:
HeaderMap: Multi-value per key, case-insensitive, insertion order preservedHashMap: Single value per key, case-sensitive, undefined orderMulti-value support:
append(): Adds value to existing headerget(): Returns first value onlyget_all(): Returns iterator over all valuesinsert(): Replaces all valuesCase-insensitive keys:
HeaderMap: "Content-Type", "content-type", "CONTENT-TYPE" are equivalentHashMap: These are three different keysHTTP-specific features:
HeaderValue type ensures valid ASCII HTTP header valueskeys_len() counts unique names, len() counts total entriesWhen to use HeaderMap:
When to use HashMap:
Key insight: HeaderMap is not just a HashMap with extra featuresāit's a specialized data structure that models HTTP header semantics correctly. HTTP headers are fundamentally different from generic key-value stores: multiple Set-Cookie headers are distinct headers with the same name, not a single header with multiple values; header names are case-insensitive by specification; and header order can matter for certain headers like Set-Cookie. Using HashMap<String, String> for HTTP headers would lose information, corrupt semantics, and produce invalid HTTP messages.