Liking cljdoc? Tell your friends :D

liblevenshtein-java vs liblevenshtein-rust Feature Comparison

Date: 2025-10-24 Purpose: Identify beneficial features from the Java version that could enhance the Rust implementation

Executive Summary

The Rust implementation has excellent feature parity with the Java version for core functionality, and has already surpassed the Java version in several areas through the recent optimization work (40-60% performance improvements). However, there are some beneficial features from the Java version that could enhance the Rust implementation.

Core Feature Parity ✅

Already Implemented in Rust

FeatureJavaRustNotes
Standard LevenshteinBoth have full implementation
TranspositionOptimal string alignment (restricted Damerau)
Merge/SplitBoth fully implemented
Candidate with distanceBoth return term + distance
Dictionary abstractionJava: IDictionary, Rust: Dictionary trait
Lazy evaluationBoth use iterators
State poolingRust recently added in Phase 5!
Transducer queriesCore functionality matches

Missing Features in Rust (Potential Additions)

1. ⭐ Dictionary Serialization (High Priority)

Java Implementation:

  • ProtobufSerializer - Binary serialization using Protocol Buffers
  • BytecodeSerializer - JVM bytecode serialization
  • PlainTextSerializer - Text file serialization
  • Can save/load pre-built dictionaries for faster startup

Benefit for Rust:

  • Fast startup times: Pre-serialize large dictionaries
  • Memory efficiency: Load binary formats directly
  • Cross-platform: Share dictionaries between systems
  • Use cases: Production deployments with large dictionaries

Recommended Implementation:

// Using serde for flexibility
pub trait DictionarySerializer {
    fn serialize<W: Write>(&self, dict: &impl Dictionary, writer: W) -> Result<()>;
    fn deserialize<R: Read>(reader: R) -> Result<Box<dyn Dictionary>>;
}

// Implementations:
// 1. BincodeSerializer (fast binary)
// 2. JsonSerializer (human-readable)
// 3. MessagePackSerializer (compact)

Priority: HIGH - Would significantly improve production usability


2. ⭐ DAWG Dictionary Implementation (High Priority)

Java Implementation:

  • SortedDawg - Directed Acyclic Word Graph
  • Space-efficient dictionary representation
  • Optimized for sorted word lists
  • Factory pattern for construction

Benefit for Rust:

  • Better memory efficiency than PathMap for certain datasets
  • Faster construction from sorted word lists
  • Alternative for different use cases: DAWG vs Trie trade-offs

Current Rust Status:

  • Has PathMapDictionary (Trie-based using PathMap)
  • Missing DAWG alternative

Recommended Implementation:

pub struct DawgDictionary {
    // Minimal automaton representation
    // Share common suffixes
    // Ideal for sorted dictionaries
}

Priority: HIGH - Provides important alternative to PathMap


3. Builder Pattern (Medium Priority)

Java Implementation:

final ITransducer<Candidate> transducer =
    new TransducerBuilder()
        .dictionary(dictionary)
        .algorithm(Algorithm.TRANSPOSITION)
        .defaultMaxDistance(2)
        .includeDistance(true)
        .build();

Benefit for Rust:

  • Improved API ergonomics: More discoverable configuration
  • Validation: Build-time validation of configuration
  • Flexibility: Easy to add new options without breaking API

Current Rust Status:

// Currently: Direct construction
let transducer = Transducer::new(dict, Algorithm::Transposition);

Recommended Implementation:

let transducer = TransducerBuilder::new()
    .dictionary(dict)
    .algorithm(Algorithm::Transposition)
    .max_distance(2)
    .build()?;

Priority: MEDIUM - Nice-to-have for API improvement


4. Benchmark/Metrics Support (Low Priority)

Java Implementation:

  • Built-in benchmarking utilities
  • Performance metrics collection
  • Profiling support

Current Rust Status:

  • Has excellent Criterion-based benchmarks
  • Has detailed profiling setup
  • Actually BETTER than Java in this area!

Priority: LOW - Rust already has superior benchmarking


5. CLI Tool (Low Priority)

Java Implementation:

  • liblevenshtein-java-cli - Separate CLI tool
  • Interactive queries
  • File-based dictionary loading

Benefit for Rust:

  • Demo/testing utility: Easy to try the library
  • Debugging tool: Test queries interactively
  • Use case: Standalone spelling correction tool

Recommended Implementation:

// examples/cli.rs or separate binary crate
liblevenshtein query --dict words.txt --term "test" --distance 2
liblevenshtein server --dict words.txt --port 8080

Priority: LOW - Nice-to-have utility, not core library feature


Features Where Rust EXCEEDS Java

1. ✅ Performance Optimizations (Rust Advantage!)

Rust Advantages:

  • StatePool optimization: Eliminates allocation overhead (Java had this, Rust improved it)
  • Arc path sharing: 72% reduction in Intersection cloning
  • Lazy edge iterator: Eliminated 27% dictionary overhead
  • 40-60% faster than baseline after Phase 1-6 optimizations
  • Copy semantics: Position is Copy (17 bytes), eliminates clone overhead

Java Status:

  • Has state pooling (original design)
  • Does not have Arc-equivalent path sharing optimization
  • Does not have specialized lazy iterator optimizations

Result: Rust is likely significantly faster than Java after recent optimizations!


2. ✅ Type Safety (Rust Advantage!)

Rust Advantages:

  • Compile-time guarantees: No null pointers, no runtime type errors
  • Trait-based design: Better abstraction without runtime overhead
  • Zero-cost abstractions: Generic iterators with no boxing

Java Limitations:

  • Null pointer exceptions
  • Type erasure
  • Boxing overhead for primitives

3. ✅ Memory Safety (Rust Advantage!)

Rust Advantages:

  • No GC pauses: Deterministic performance
  • Ownership system: Prevents memory leaks and data races
  • Thread safety: Compile-time verification

Java Limitations:

  • Garbage collector pauses
  • Potential memory leaks from forgotten references
  • Thread safety requires runtime checks

Recommended Roadmap for Rust Enhancements

Phase 1: High-Priority Features (Production Readiness)

  1. Dictionary Serialization (2-3 weeks)

    • Implement serde-based serialization
    • Support for bincode, JSON, MessagePack
    • Benchmark serialization performance
    • Add examples and documentation
  2. DAWG Dictionary (3-4 weeks)

    • Implement DAWG construction algorithm
    • Benchmark vs PathMap for various datasets
    • Document trade-offs and use cases
    • Ensure Dictionary trait compatibility

Phase 2: Medium-Priority Features (API Improvement)

  1. Builder Pattern (1 week)
    • Implement TransducerBuilder
    • Add validation and helpful error messages
    • Update examples and documentation
    • Maintain backward compatibility

Phase 3: Low-Priority Features (Nice-to-Have)

  1. CLI Tool (1-2 weeks)
    • Create liblevenshtein-cli binary
    • Support file-based dictionaries
    • Add serialization support
    • Interactive query mode

Conclusion

The Rust implementation has excellent core feature parity with the Java version and has exceeded it in several critical areas (performance, type safety, memory safety). The primary gaps are:

High Priority:

  1. Dictionary serialization (critical for production use)
  2. DAWG dictionary implementation (important alternative)

Medium Priority: 3. Builder pattern (API ergonomics)

Low Priority: 4. CLI tool (utility, not core library)

The Rust version is in excellent shape and the recent optimization work (Phases 1-6) has likely made it significantly faster than the Java version. The recommended additions would make it even more production-ready and feature-complete.

Appendix: Java Package Structure

com.github.liblevenshtein/
├── collection/
│   ├── dictionary/
│   │   ├── SortedDawg.java
│   │   ├── Dawg.java
│   │   ├── DawgNode.java
│   │   ├── FinalDawgNode.java
│   │   └── factory/
│   │       └── DawgFactory.java
│   └── AbstractIterator.java
├── distance/
│   ├── IDistance.java
│   ├── StandardDistance.java
│   └── TranspositionDistance.java
├── serialization/
│   ├── Serializer.java
│   ├── ProtobufSerializer.java
│   ├── BytecodeSerializer.java
│   └── PlainTextSerializer.java
└── transducer/
    ├── Algorithm.java
    ├── Candidate.java
    ├── ITransducer.java
    ├── Transducer.java
    ├── State.java
    ├── Position.java
    └── factory/
        ├── TransducerBuilder.java
        ├── StateFactory.java
        └── CandidateFactory.java

Appendix: Rust Module Structure (Current)

liblevenshtein/
├── dictionary/
│   ├── mod.rs (Dictionary, DictionaryNode traits)
│   └── pathmap.rs (PathMapDictionary)
├── distance/
│   └── mod.rs (standard_distance, transposition_distance)
└── transducer/
    ├── algorithm.rs
    ├── candidate.rs (Candidate struct)
    ├── mod.rs (Transducer)
    ├── query.rs (QueryIterator, CandidateIterator)
    ├── state.rs
    ├── position.rs
    ├── pool.rs (StatePool - Phase 5 addition!)
    ├── intersection.rs
    └── transition.rs

Missing from Rust:

  • serialization/ module
  • dictionary/dawg.rs implementation
  • Builder pattern utilities

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close