rust
40 lines · 8 steps
Building URL slugs in Rust
Turn arbitrary titles into clean, collision-free URL slugs by lowercasing, transliterating accents, and appending numeric suffixes.
Explained by
highlit
1use once_cell::sync::Lazy;
2use regex::Regex;
3
4static NON_ALPHANUMERIC: Lazy<Regex> = Lazy::new(|| Regex::new(r"[^a-z0-9]+").unwrap());
5
6fn slugify(title: &str) -> String {
7 let lowered = title.to_lowercase();
8
9 let normalized: String = lowered
10 .chars()
11 .map(|c| match c {
12 'à' | 'á' | 'â' | 'ã' | 'ä' | 'å' => 'a',
13 'è' | 'é' | 'ê' | 'ë' => 'e',
14 'ì' | 'í' | 'î' | 'ï' => 'i',
15 'ò' | 'ó' | 'ô' | 'õ' | 'ö' => 'o',
16 'ù' | 'ú' | 'û' | 'ü' => 'u',
17 'ñ' => 'n',
18 'ç' => 'c',
19 'ß' => 's',
20 other => other,
21 })
22 .collect();
23
24 let slug = NON_ALPHANUMERIC.replace_all(&normalized, "-");
25 slug.trim_matches('-').to_string()
26}
27
28fn slugify_unique(title: &str, existing: &[String]) -> String {
29 let base = slugify(title);
30 if base.is_empty() {
31 return "untitled".to_string();
32 }
33 if !existing.iter().any(|s| s == &base) {
34 return base;
35 }
36 (2..)
37 .map(|n| format!("{base}-{n}"))
38 .find(|candidate| !existing.iter().any(|s| s == candidate))
39 .unwrap()
40}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1A lazily compiled static regex avoids recompiling the pattern on every call.
- 2Transliterating accented characters before stripping non-alphanumerics keeps slugs readable instead of dropping letters.
- 3An unbounded numeric range makes finding a unique suffix a simple, allocation-light search.
Related explainers
rust
use axum::{ extract::{FromRequestParts, Query}, http::{request::Parts, StatusCode}, };
Building a custom Axum extractor for query filters
extractors
query-parsing
enums
Intermediate
8 steps
rust
use std::sync::Arc; use axum::{ extract::{Multipart, State}, http::StatusCode,
Throttling file uploads in Axum with a Semaphore
concurrency
backpressure
multipart
Advanced
8 steps
java
public final class CaseConverter { private static final Pattern CAMEL_BOUNDARY = Pattern.compile("([a-z0-9])([A-Z])|([A-Z]+)([A-Z][a-z])");
Converting camelCase to snake_case in Java
regex
string-manipulation
utility-class
Intermediate
7 steps
rust
#[derive(Debug, Clone, PartialEq)] pub enum Token { Number(f64), Plus,
How a tokenizer turns text into tokens
lexing
enums
iterators
Intermediate
8 steps
python
import pandas as pd import numpy as np
Cleaning a customer DataFrame with pandas
data-cleaning
regex
normalization
Intermediate
9 steps
java
public final class EmailNormalizer { private static final Pattern EMAIL_PATTERN = Pattern.compile( "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$"
Normalizing email addresses in Java
validation
regex
normalization
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/building-url-slugs-in-rust-explained-rust-d6fa/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.