Loading page…
Rust walkthroughs
Loading page…
http::HeaderMap handle multiple values for the same header name compared to HashMap?http::HeaderMap is specifically designed for HTTP headers where multiple values for the same header name are both valid and common, such as Set-Cookie headers or multiple Accept values. Unlike HashMap, which stores a single value per key and overwrites on insertion, HeaderMap maintains all values for a given header name in insertion order. When you insert a value for an existing header name, HeaderMap appends the value rather than replacing it. Retrieval methods like get() return the first value for backward compatibility, while get_all() returns an iterator over all values. This design reflects HTTP semantics where headers like Set-Cookie must each be sent on their own line, and order can be significant for headers like Accept-Encoding.
use std::collections::HashMap;
fn hashmap_single_value() {
let mut headers: HashMap<&str, &str> = HashMap::new();
// Insert first value
headers.insert("content-type", "text/html");
println!("{:?}", headers.get("content-type")); // Some("text/html")
// Insert second value - REPLACES the first
headers.insert("content-type", "application/json");
println!("{:?}", headers.get("content-type")); // Some("application/json")
// Only one value exists
println!("Length: {}", headers.len()); // 1
println!("Values: {:?}", headers.values().collect::<Vec<_>>()); // ["application/json"]
}HashMap overwrites previous values when inserting with an existing key.
use http::HeaderMap;
fn headermap_multiple_values() {
let mut headers = HeaderMap::new();
// Insert first value
headers.insert("content-type", "text/html".parse().unwrap());
println!("{:?}", headers.get("content-type")); // Some("text/html")
// Append second value - KEEPS BOTH
headers.append("content-type", "application/json".parse().unwrap());
println!("{:?}", headers.get("content-type")); // Some("text/html") - first value
// Get all values
let all: Vec<_> = headers.get_all("content-type").iter().collect();
println!("All values: {:?}", all); // ["text/html", "application/json"]
// Header name count is different from value count
println!("Header names: {}", headers.keys_name_count()); // 1
println!("Total values: {}", headers.values().count()); // 2
}HeaderMap preserves all values; insert replaces, append adds.
use http::HeaderMap;
fn insert_vs_append() {
let mut headers = HeaderMap::new();
// insert() replaces all existing values
headers.insert("x-custom", "first".parse().unwrap());
headers.insert("x-custom", "second".parse().unwrap());
let values: Vec<_> = headers.get_all("x-custom").iter().collect();
println!("After inserts: {:?}", values); // ["second"] - first was replaced
// append() adds to existing values
headers.append("x-custom", "third".parse().unwrap());
headers.append("x-custom", "fourth".parse().unwrap());
let values: Vec<_> = headers.get_all("x-custom").iter().collect();
println!("After appends: {:?}", values); // ["second", "third", "fourth"]
// insert() clears and sets new value
headers.insert("x-custom", "fifth".parse().unwrap());
let values: Vec<_> = headers.get_all("x-custom").iter().collect();
println!("After final insert: {:?}", values); // ["fifth"] - all previous cleared
}insert replaces all values; append preserves and adds.
use http::HeaderMap;
fn realistic_http_headers() {
let mut response_headers = HeaderMap::new();
// Set-Cookie must be sent as separate headers
// Browsers won't accept multiple cookies on one Set-Cookie line
response_headers.append("set-cookie", "session=abc123; HttpOnly".parse().unwrap());
response_headers.append("set-cookie", "preferences=dark; Max-Age=86400".parse().unwrap());
response_headers.append("set-cookie", "tracking=xyz; Secure".parse().unwrap());
// All Set-Cookie values preserved
for cookie in response_headers.get_all("set-cookie") {
println!("Set-Cookie: {}", cookie.to_str().unwrap());
}
// Accept headers can have multiple values with different priorities
let mut request_headers = HeaderMap::new();
request_headers.append("accept", "text/html".parse().unwrap());
request_headers.append("accept", "application/json".parse().unwrap());
request_headers.append("accept", "*/*".parse().unwrap());
// Order matters for content negotiation
println!("Accept header order:");
for (i, accept) in request_headers.get_all("accept").iter().enumerate() {
println!(" {}. {}", i + 1, accept.to_str().unwrap());
}
}HTTP semantics require multiple values for headers like Set-Cookie.
use http::HeaderMap;
use std::collections::HashMap;
fn iteration_comparison() {
// HashMap - each key-value pair appears once
let mut hashmap: HashMap<&str, &str> = HashMap::new();
hashmap.insert("a", "1");
hashmap.insert("b", "2");
println!("HashMap iteration:");
for (key, value) in &hashmap {
println!(" {} = {}", key, value);
}
// a = 1
// b = 2
// HeaderMap - each VALUE appears as separate entry
let mut headermap = HeaderMap::new();
headermap.append("a", "1".parse().unwrap());
headermap.append("a", "1b".parse().unwrap()); // Second value for "a"
headermap.append("b", "2".parse().unwrap());
println!("\nHeaderMap iteration:");
for (name, value) in &headermap {
println!(" {} = {}", name, value.to_str().unwrap());
}
// a = 1
// a = 1b
// b = 2
println!("\nHeaderMap keys_name_count: {}", headermap.keys_name_count()); // 2
println!("HeaderMap iter count: {}", headermap.iter().count()); // 3
}HeaderMap iteration yields each value separately, even for the same header name.
use http::HeaderMap;
use http::header::{CONTENT_TYPE, USER_AGENT};
fn case_insensitive_keys() {
let mut headers = HeaderMap::new();
// Header names are case-insensitive in HTTP
headers.insert("Content-Type", "text/html".parse().unwrap());
// These all access the same entry
println!("{:?}", headers.get("content-type")); // Some("text/html")
println!("{:?}", headers.get("Content-Type")); // Some("text/html")
println!("{:?}", headers.get("CONTENT-TYPE")); // Some("text/html")
// Using typed constants
headers.insert(USER_AGENT, "MyApp/1.0".parse().unwrap());
println!("{:?}", headers.get(USER_AGENT)); // Some("MyApp/1.0")
// Appending works with any case
headers.append("content-type", "charset=utf-8".parse().unwrap());
let values: Vec<_> = headers.get_all("CONTENT-TYPE").iter().collect();
println!("All content-type values: {:?}", values);
}HeaderMap treats header names case-insensitively, matching HTTP semantics.
use http::HeaderMap;
fn retrieval_methods() {
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 the FIRST value only
let first = headers.get("accept");
println!("First value: {:?}", first.unwrap().to_str().unwrap()); // "text/html"
// get_all() returns an iterator over ALL values
println!("All values:");
for value in headers.get_all("accept") {
println!(" - {}", value.to_str().unwrap());
}
// Convert to Vec if needed
let all_values: Vec<_> = headers
.get_all("accept")
.iter()
.map(|v| v.to_str().unwrap().to_string())
.collect();
println!("As vector: {:?}", all_values);
// Checking if header exists
println!("Has accept: {}", headers.contains_key("accept"));
println!("Has content-type: {}", headers.contains_key("content-type"));
}get returns the first value; get_all iterates over all values.
use http::HeaderMap;
fn removal_behavior() {
let mut headers = HeaderMap::new();
headers.append("x-custom", "first".parse().unwrap());
headers.append("x-custom", "second".parse().unwrap());
headers.append("x-custom", "third".parse().unwrap());
// remove() removes ALL values for the key
let removed = headers.remove("x-custom");
println!("Removed first value: {:?}", removed.unwrap().to_str().unwrap()); // "first"
// All values are gone
println!("Remaining values: {:?}", headers.get("x-custom")); // None
// remove_entry() also returns all values
headers.append("x-other", "a".parse().unwrap());
headers.append("x-other", "b".parse().unwrap());
let (name, first_value) = headers.remove_entry("x-other").unwrap();
println!("Removed entry: {} = {}", name, first_value.to_str().unwrap());
}remove deletes all values for a header name.
use http::HeaderMap;
use std::collections::HashMap;
fn performance_comparison() {
// HashMap memory: ~48 bytes overhead per entry + key + value
// HashMap lookup: O(1) average
// HeaderMap memory: Optimized for small header counts
// HeaderMap lookup: O(n) for small n, but very cache-friendly
// HeaderMap stores values inline when possible
// HashMap: each entry is independent
let mut hashmap: HashMap<String, String> = HashMap::new();
for i in 0..100 {
hashmap.insert(format!("header-{}", i), format!("value-{}", i));
}
// HeaderMap: values for same key stored together
let mut headermap = HeaderMap::with_capacity(100);
for i in 0..50 {
// Multiple values per key
headermap.append(format!("header-{}", i), format!("value-{}a", i).parse().unwrap());
headermap.append(format!("header-{}", i), format!("value-{}b", i).parse().unwrap());
}
// HeaderMap is optimized for typical HTTP header counts (5-50 headers)
// HashMap is optimized for general-purpose key-value storage
println!("HashMap len: {}", hashmap.len()); // 100
println!("HeaderMap keys: {}", headermap.keys_name_count()); // 50
println!("HeaderMap values: {}", headermap.len()); // 100
}HeaderMap is optimized for HTTP header patterns; HashMap for general use.
use http::HeaderMap;
use std::collections::HashMap;
fn entry_api() {
// HashMap entry API - returns single value entry
let mut hashmap: HashMap<&str, i32> = HashMap::new();
hashmap.entry("count").and_modify(|v| *v += 1).or_insert(1);
hashmap.entry("count").and_modify(|v| *v += 1).or_insert(1);
println!("HashMap count: {:?}", hashmap.get("count")); // Some(2)
// HeaderMap entry API - different semantics
let mut headermap = HeaderMap::new();
// entry() returns Entry for the header name
use http::header::Entry;
// This replaces existing values
if let Entry::Vacant(entry) = headermap.entry("count") {
entry.insert("1".parse().unwrap());
}
// append is simpler for multiple values
headermap.append("count", "2".parse().unwrap());
println!("HeaderMap count values:");
for v in headermap.get_all("count") {
println!(" {}", v.to_str().unwrap());
}
}HashMap entries manage single values; HeaderMap has different entry semantics.
use http::HeaderMap;
use std::collections::HashMap;
fn conversions() {
// HeaderMap to HashMap (loses multiple values)
let mut headermap = HeaderMap::new();
headermap.append("accept", "text/html".parse().unwrap());
headermap.append("accept", "application/json".parse().unwrap());
let hashmap: HashMap<_, _> = headermap
.iter()
.map(|(k, v)| (k.to_string(), v.to_str().unwrap().to_string()))
.collect();
println!("HashMap only has last value per key");
// HashMap loses "text/html" if "accept" was the key
// Better: HeaderMap to HashMap<String, Vec<String>>
let multi_map: HashMap<String, Vec<String>> = {
let mut map = HashMap::new();
for (name, value) in &headermap {
map.entry(name.to_string())
.or_insert_with(Vec::new)
.push(value.to_str().unwrap().to_string());
}
map
};
println!("Multi-map: {:?}", multi_map);
// {"accept": ["text/html", "application/json"]}
}Converting to HashMap requires deciding how to handle multiple values.
| Feature | HashMap | HeaderMap |
|---------|-----------|-------------|
| Multiple values per key | No (overwrites) | Yes (appends) |
| Key case sensitivity | Sensitive | Insensitive |
| insert() | Replaces value | Replaces all values |
| append() | N/A | Adds value |
| get() | Returns value | Returns first value |
| get_all() | N/A | Returns all values |
| Iteration | Key-value pairs | One entry per value |
| Key type | Any hashable | HeaderName |
| Typical use | General KV storage | HTTP headers |
HeaderMap and HashMap serve fundamentally different purposes:
HashMap design:
insert overwritingget returns the only valueHeaderMap design:
insert replaces all values, append adds valuesget returns first value, get_all returns all valuesKey insight: The HTTP/1.1 specification allows multiple header fields with the same name (except for some headers like Content-Type), and the order of headers can be significant. HeaderMap is purpose-built for these semantics, while HashMap would silently drop values or require workarounds like storing Vec<String> values. When building HTTP servers or clients, HeaderMap correctly represents the wire format where multiple Set-Cookie headers each need their own line, and Accept values are considered in order for content negotiation. Use HashMap for general configuration or data storage; use HeaderMap when dealing with actual HTTP headers.