rust 48 lines · 8 steps

Parsing a URL query string in Rust

A byte-level percent-decoder feeds an iterator pipeline that splits a query string into a key-value map.

Explained by highlit
1use std::collections::HashMap;
2 
3fn decode_component(input: &str) -> String {
4 let bytes = input.as_bytes();
5 let mut out = Vec::with_capacity(bytes.len());
6 let mut i = 0;
7 
8 while i < bytes.len() {
9 match bytes[i] {
10 b'+' => {
11 out.push(b' ');
12 i += 1;
13 }
14 b'%' if i + 2 < bytes.len() => {
15 let hi = (bytes[i + 1] as char).to_digit(16);
16 let lo = (bytes[i + 2] as char).to_digit(16);
17 match (hi, lo) {
18 (Some(hi), Some(lo)) => {
19 out.push((hi * 16 + lo) as u8);
20 i += 3;
21 }
22 _ => {
23 out.push(bytes[i]);
24 i += 1;
25 }
26 }
27 }
28 b => {
29 out.push(b);
30 i += 1;
31 }
32 }
33 }
34 
35 String::from_utf8_lossy(&out).into_owned()
36}
37 
38pub fn parse_query(query: &str) -> HashMap<String, String> {
39 query
40 .trim_start_matches('?')
41 .split('&')
42 .filter(|pair| !pair.is_empty())
43 .map(|pair| match pair.split_once('=') {
44 Some((k, v)) => (decode_component(k), decode_component(v)),
45 None => (decode_component(pair), String::new()),
46 })
47 .collect()
48}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Walking raw bytes with a manual index gives precise control over multi-byte escape sequences like percent-encoding.
  2. 2Guarded match arms let you branch on both a byte's value and surrounding conditions in one expression.
  3. 3An iterator pipeline ending in collect turns a query string into a HashMap without any explicit loop.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Parsing a URL query string in Rust — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code