Version: 0.10.0 Last Updated: 2026-08-03
This guide will help you get started with liblevenshtein-rust for fast approximate string matching.
At a high level, a query builds a Levenshtein automaton $A(W, k)$ from your search
term $W$ and error bound $k$, then walks it in lock-step with the dictionary to
yield every term within edit distance $k$:
Add to your Cargo.toml:
[dependencies]
liblevenshtein = "0.10"
SIMD (AVX2/SSE4.1) is enabled automatically on x86_64 targets via runtime CPU feature detection — no feature flag required.
Version 0.10.0 is not published yet. Build the sibling
liblevenshtein-rust-cli
checkout while the coordinated release is being prepared. After publication:
cargo install liblevenshtein-cli
Future pre-built packages will be available from the
liblevenshtein-rust-cli releases page:
.deb packages.rpm packages.pkg.tar.zst packages.tar.gz and .zip archives for Linux and macOS (x86_64 and ARM64)use liblevenshtein::prelude::*;
// Create a dictionary from terms (using DoubleArrayTrie for best performance)
let terms = vec!["test", "testing", "tested", "tester"];
let dict = DoubleArrayTrie::from_terms(terms);
// Create a transducer with Standard algorithm
let transducer = Transducer::new(dict, Algorithm::Standard);
// Query for terms within edit distance 2
for term in transducer.query("tset", 2) {
println!("Match: {}", term);
}
// Query with distances
for candidate in transducer.query_with_distance("tset", 2) {
println!("Match: {} (distance: {})", candidate.term, candidate.distance);
}
Output:
Match: test
Match: tester
Match: test (distance: 1)
Match: tester (distance: 2)
For correct character-level Levenshtein distances with Unicode text, use the character-level dictionary variants:
use liblevenshtein::prelude::*;
// Create a character-level dictionary for Unicode support
let terms = vec!["café", "naïve", "日本語", "emoji😀"];
let dict = DoubleArrayTrieChar::from_terms(terms);
// Create transducer
let transducer = Transducer::new(dict, Algorithm::Standard);
// Query with Unicode strings
for candidate in transducer.query_with_distance("café", 1) {
println!("{}: distance {}", candidate.term, candidate.distance);
}
Note: Character-level dictionaries (DoubleArrayTrieChar, PathMapDictionaryChar) have ~5% performance overhead and use 4x memory for edge labels compared to byte-level variants, but provide correct Unicode Levenshtein distances.
liblevenshtein supports three Levenshtein distance algorithms:
use liblevenshtein::prelude::*;
let dict = DoubleArrayTrie::from_terms(vec!["test", "testing"]);
// Standard: insert, delete, substitute
let standard = Transducer::new(dict.clone(), Algorithm::Standard);
// Transposition: adds character transposition (swap adjacent chars)
let transposition = Transducer::new(dict.clone(), Algorithm::Transposition);
// Merge and Split: adds merge and split operations
let merge_split = Transducer::new(dict, Algorithm::MergeAndSplit);
When to use each:
For applications like code completion, you want results sorted by relevance (distance first, then alphabetically):
use liblevenshtein::prelude::*;
let dict = DoubleArrayTrie::from_terms(vec![
"test", "testing", "tested", "tester", "best", "rest"
]);
let transducer = Transducer::new(dict, Algorithm::Standard);
// Get results sorted by distance, then lexicographically
for candidate in transducer.query_with_distance("tset", 2).sorted() {
println!("{}: {}", candidate.term, candidate.distance);
}
Output:
test: 1
best: 2
rest: 2
tester: 2
tested: 2
testing: 2
Enable prefix mode for autocomplete-style matching:
use liblevenshtein::prelude::*;
let dict = DoubleArrayTrie::from_terms(vec![
"test", "testing", "tested", "tester", "apple", "banana"
]);
let transducer = Transducer::new(dict, Algorithm::Standard);
// Only match terms starting with "tes"
for candidate in transducer
.query_with_distance("test", 1)
.sorted()
.with_prefix("tes")
{
println!("{}: {}", candidate.term, candidate.distance);
}
Output:
test: 0
tested: 1
tester: 1
testing: 1
liblevenshtein queries any backend from the libdictenstein
crate (re-exported here; the *Char variants are UTF-8 / char-level):
| Backend | Best For | Reads | Updates |
|---|---|---|---|
| DoubleArrayTrie(Char) (default) | static dictionaries, fastest queries ($\mathcal{O}(1)$ transitions) | wait-free | No |
| DynamicDawg(Char) | general dynamic use; SIMD + bloom pruning | lock-free | Yes |
| DynamicDawgU64 | 64-bit labels / hashes | lock-free (ArcSwap) | Yes |
| SuffixAutomaton(Char) | substring / infix matching | lock-free | Yes |
| Scdawg(Char) | bidirectional substring (backs WallBreaker) | lock-free | Yes |
| PathMapDictionary(Char) | update-heavy workloads (persistent structural-sharing map) | persistent | Yes |
| BijectiveMap | term ↔ integer id (both directions) | — | Yes |
| PersistentARTrie(Char / U64) | huge / durable prefix dictionaries (disk-persisted, mmap) | lock-free CAS | Yes |
| PersistentScdawg / PersistentSuffixAutomaton / PersistentSuffixTree(Char) | huge / durable substring dictionaries (disk-persisted) | lock-free | Yes |
| PersistentVocabARTrie | huge / durable term ↔ id vocabulary (disk-persisted) | lock-free | Yes |
Persistent $
\ne$ static. ThePersistent*family persists to disk (durable, non-volatile, memory-mapped) and is fully dynamic (concurrent insert/remove); onlyDoubleArrayTrieis read-only after build.
Recommendations:
DoubleArrayTrie for the fastest queries over a static dictionary.*Char variant for correct char-level distances.DynamicDawg (or DynamicDawgU64 for lock-free reads).SuffixAutomaton; bidirectional / large-k: Scdawg.Persistent* family — PersistentARTrie (prefix), PersistentScdawg / PersistentSuffixAutomaton (substring), PersistentVocabARTrie (vocabulary); all dynamic.BijectiveMap (in-memory) or PersistentVocabARTrie (on disk).For a decision tree, see the backend selection diagram and the backends guide.
The Examples & Tutorials index walks through the library
step by step. The runnable programs live in the examples/ directory:
spell_checker.rs — simple fuzzy matchingordered_query_demo.rs — sorted results for code completionunicode_diacritics.rs — Unicode character handlingdynamic_dictionary.rs — runtime dictionary updatesfuzzy_maps_code_completion.rs — value-mapped fuzzy lookupcontextual_completion.rs — scope-aware completionserialization.rs — save / load dictionariesRun an example:
cargo run --example spell_checker
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |