Version: 0.9.1 Last Updated: 2026-06-19
This document describes all features available in liblevenshtein-rust.
The diagram below shows how the major components stack: the dictionary backends (now provided by the libdictenstein crate) sit beneath the Levenshtein automata, intersection traversal, and query-iterator layers.
The dictionary data structures live in the libdictenstein
crate and are re-exported by liblevenshtein. The *Char variants are the UTF-8
(char/u32) counterparts of the byte-level (u8) backends. The full set is:
| Backend | Unit | Mutable | Best for |
|---|---|---|---|
DoubleArrayTrie / DoubleArrayTrieChar | u8 / char | No (read-only) | Static dictionaries, fastest reads (default) |
DynamicDawg / DynamicDawgChar | u8 / char | Yes | Runtime insert/delete with space efficiency |
DynamicDawgU64 | u64 | Yes | Token/ID streams keyed by 64-bit units |
SuffixAutomaton / SuffixAutomatonChar | u8 / char | No | Substring search |
Scdawg / ScdawgChar | u8 / char | No | Compacted DAWG with shared suffixes |
PersistentARTrie / PersistentARTrieChar | u8 / char | Persistent | Lock-free snapshots via structural sharing |
PathMapDictionary | u8 | Yes | General-purpose trie with structural sharing |
See the Backends guide for a full comparison and decision tree.
use liblevenshtein::prelude::*;
let dict = DoubleArrayTrie::from_terms(vec!["test", "testing"]);
LockFreeDawg core); writes via compare_exchangeuse liblevenshtein::prelude::*;
let dict = DynamicDawg::from_terms(vec!["test", "testing"]);
dict.insert("tester"); // Online insertion with minimization
dict.remove("test"); // Online deletion
Arc<ArcSwap<…>>); writes publish by atomic swapuse liblevenshtein::prelude::*;
let dict = PathMapDictionary::from_iter(vec!["test", "testing"]);
Fluent API for creating transducers with validation:
use liblevenshtein::prelude::*;
let transducer = TransducerBuilder::new()
.dictionary(dict)
.algorithm(Algorithm::Transposition)
.build()?;
Benefits:
for term in transducer.query("test", 2) {
println!("{}", term);
}
for candidate in transducer.query_with_distance("test", 2) {
println!("{}: {}", candidate.term, candidate.distance);
}
for candidate in transducer.query_ordered("aple", 1) {
println!("{}: {}", candidate.term, candidate.distance);
}
// Output:
// ape: 1
// apple: 1
// apply: 1
\pm$ edits// Prefix matching for code completion
for candidate in transducer
.query_ordered("getVal", 1)
.prefix() // Only terms starting with "getVal" ± 1 edit
.filter(|c| c.term.starts_with("get"))
{
println!("{}: {}", candidate.term, candidate.distance);
}
See the Code Completion Guide for detailed examples.
Enable with: features = ["serialization"]
Supported formats:
protobuf feature): Portable binary schemaCompression support (v0.2.0, optional compression feature):
GzipSerializer<S> wraps a supported binary serializerUsage:
use liblevenshtein::prelude::*;
use liblevenshtein::serialization::*;
use std::fs::File;
// Save dictionary with compression
let dict = PathMapDictionary::from_iter(vec!["test", "testing"]);
let file = File::create("dict.bin.gz")?;
GzipSerializer::<BincodeSerializer>::serialize(&dict, file)?;
// Load compressed dictionary
let file = File::open("dict.bin.gz")?;
let loaded: PathMapDictionary = GzipSerializer::<BincodeSerializer>::deserialize(file)?;
// Protobuf format (cross-language)
#[cfg(feature = "protobuf")]
{
let file = File::create("dict.pb.gz")?;
GzipSerializer::<ProtobufSerializer>::serialize(&dict, file)?;
}
Benefits:
The executable and REPL moved to the separate
liblevenshtein-cli
package in 0.10. Version 0.10.0 is not published yet; build the sibling checkout
until the coordinated library-first release is complete. After publication,
install it with cargo install liblevenshtein-cli.
The library has undergone extensive optimization work:
See the optimization summary documentation for details.
Run benchmarks:
RUSTFLAGS="-C target-cpu=native" cargo bench
\sim\mathcal{O}(n)$ for $n$ unique prefixes\sim\mathcal{O}(m)$ for $m$ unique substrings (shares prefixes and suffixes)All dictionary implementations are thread-safe, with lock-free reads (a reader never blocks on a writer):
Arc<ArcSwap<PathMapState>> — lock-free reads; writes publish a new state by atomic swapLockFreeDawg revisions — wait-free reads; writers path-copy and CAS-publish a new rootSee Thread Safety for the full per-backend concurrency model.
| Feature | Java | Rust | Notes |
|---|---|---|---|
| Standard Levenshtein | ✅ | ✅ | Full parity |
| Transposition | ✅ | ✅ | Full parity |
| Merge/Split | ✅ | ✅ | Full parity |
| Dictionary abstraction | ✅ | ✅ | Trait-based in Rust |
| DAWG dictionary | ✅ | ✅ | New in Rust! |
| PathMap/Trie | ✅ | ✅ | Full parity |
| Serialization | ✅ | ✅ | New in Rust! |
| Builder pattern | ✅ | ✅ | New in Rust! |
| CLI tool | ✅ | ✅ | Separate liblevenshtein-cli package |
| State pooling | ✅ | ✅ | Enhanced in Rust! |
| Performance | Good | Excellent | 40-60% faster after optimizations |
libdictenstein: Dictionary backends (double-array tries, DAWGs, suffix automata, persistent tries, PathMap)smallvec: Stack-allocated vectorsserde, bincode: Binary serialization (feature: serialization)prost: Protocol Buffers (feature: protobuf)rayon: Parallel in-memory phonetic matching (feature: parallel-grep)criterion: Benchmarking[features]
default = ["parking_lot"]
serialization = ["serde", "bincode", "libdictenstein/serialization"]
compression = ["flate2", "serialization", "libdictenstein/compression"]
protobuf = ["prost", "bytes", "prost-build", "serialization"]
phonetic-rules = ["unicode-normalization"]
parallel-grep = ["rayon", "phonetic-rules"]
Feature combinations:
serialization: Compact bincode save/load supportserialization,compression: Add gzip compressionserialization,protobuf: Add cross-language Protobuf supportphonetic-rules,parallel-grep: Parallel in-memory phonetic matchingCan 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 |