Library / SDK
mitsuhiko/similar avatar
mitsuhiko/similar

similar: a diffing library for Rust with six algorithms

A high level diffing library for rust based on diffs

1,337 stars58 forksRustApache-2.0

At a glance

What is it?
similar is a dependency free Rust crate that implements several diffing algorithms and layers higher level interfaces for text, bytes and structured merging on top of them. It suits anyone who needs to compute, render or merge differences, and it lets you pick the algorithm per call rather than living with one default.
Who is it for?
Adopt similar when you need diff output in Rust without pulling in a dependency tree, since the crate ships no required dependencies and exposes heuristic and raw Myers, patience, Hunt, histogram and classic LCS diffs alongside unified output and three way merging. Do not adopt it if you need diffing at the AST or semantic level, because everything here operates on sequences of comparable items rather than on meaning.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 24 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Six algorithms behind one trait, chosen per call

similar is a dependency free crate for Rust that implements different diffing algorithms and high level interfaces for them. The list of what is in the box is the reason to look at it, because most diffing libraries ship one algorithm and expose it as a fait accompli.

The algorithms are a practical heuristic Myers diff and a raw Myers diff, patience diff, a Hunt-style diff, a histogram diff, and a classic LCS table diff. On top of that sit diffing on arbitrary comparable sequences, line, word, character and grapheme level diffing, text and byte diffing, unified diff generation, and structured three-way line merging with merge and diff3 conflict output.

The point of having both heuristic and raw Myers is worth spelling out. The raw version is the algorithm as described, and the heuristic one applies the usual linear space refinement so the implementation does not blow up on large inputs. A project that cares about exactly matching a reference implementation can ask for raw, and everyone else gets the practical one.

Patience diff earns its place separately. It is the algorithm that tries to find lines that are unique in both inputs and anchors the diff on those, which is why its output tends to survive a large unrelated block moving through a file. The examples directory has a dedicated patience.rs, so you can see its shape rather than infer it.

The library was originally inspired by pijul's diff library but has since changed significantly, and it was built for the insta snapshot testing library.

The high level interface is a change iterator

The usage example shows the shape of the API, and it is a straightforward one: build a TextDiff from two strings, then walk the changes and print them with a sign per tag.

rust
use similar::{ChangeTag, TextDiff};

fn main() {
    let diff = TextDiff::from_lines(
        "Hello World\nThis is the second line.\nThis is the third.",
        "Hallo Welt\nThis is the second line.\nThis is life.\nMoar and more",
    );

    for change in diff.iter_all_changes() {
        let sign = match change.tag() {
            ChangeTag::Delete => "-",
            ChangeTag::Insert => "+",
            ChangeTag::Equal => " ",
        };
        print!("{}{}", sign, change);
    }
}

TextDiff::from_lines splits on line boundaries and produces a diff whose changes carry a ChangeTag of Delete, Insert or Equal. The iter_all_changes method yields both sides in order, which is exactly what a unified or inline renderer needs.

The naming pattern repeats across the crate. The features list an inline feature, and the examples directory contains terminal.rs, terminal-inline.rs, udiff.rs, close-matches.rs, float-buckets.rs, large.rs, nonstring.rs, original-slices.rs, owned-lookup.rs, serde.rs, whitespace.rs and perf-fuzz.rs. The example gallery at examples/README.md shows the commands and the sample output, so the shape of each renderer is visible before you write code.

The float-buckets example name alone is a hint about a real design decision: diffing floating point numbers needs a tolerance, and the library treats that as a first class case rather than leaving you to compare raw bits.

No_std with alloc, and a choice of map backend

The crate now enables std by default, which is a change worth understanding if you are on an embedded target or building a WebAssembly module. For no_std plus alloc usage, the manifest documents a single dependency line:

toml
[dependencies]
similar = { version = "3", default-features = false }

The no_std mode is not a stripped down afterthought, because the backend selection is documented. With default features off, the library uses alloc::collections::BTreeMap, and with the hashbrown feature enabled it uses hashbrown::HashMap instead. That is a real choice for a no_std environment, since BTreeMap is always available from alloc while hashbrown has to be pulled in, and the trade is ordered iteration against hash lookup speed.

The feature list in the manifest shows the whole surface: std and text are on by default, inline pulls in text, bytes pulls in bstr and text, unicode pulls in text with unicode-segmentation and bstr's unicode mode, wasm32_web_time swaps in the web-time crate, and hashbrown is opt-in. The optional dependencies are exactly those: unicode-segmentation, bstr, serde with alloc and derive but not std, web-time, and hashbrown. That is what dependency free means here in practice, the default configuration compiles with no third party crates at all.

The Makefile confirms the matrix is tested rather than claimed. The test target runs cargo test, then cargo test with all features, then three cargo check runs with no default features, with hashbrown, and with bytes, and there is a separate wasi-test target that runs the suite against wasm32-wasip1.

Unified diffs and three way merges with diff3 conflicts

Two of the more advanced capabilities are unified diff generation and structured three-way line merging with merge and diff3 conflict output.

Unified diff generation is what makes similar usable behind a git-like command, and the udiff.rs example is the demonstration. A unified diff needs hunk headers with line ranges, context control and the exact header format that patch tooling expects, and getting that right is tedious enough that most projects end up shelling out to the system diff.

Three way merging is the harder feature and the more useful one for a tool that has to reconcile two versions against a common base. The diff3 style conflict output in particular is a specific choice. Where a plain merge would silently pick one side, diff3 style output marks the base and both sides so a human, or a reviewer, can see what each change actually was. The alternative output formats for merges exist too, so the conflict presentation is a parameter of the call rather than a fixed behaviour.

This matters for the ecosystem around the crate. similar-asserts is the assertion library built on top of it, so a team that adopts both gets the same diff text in an assertion failure message that it gets in a diff view, and insta uses it for snapshot testing, which is where the library came from. A diff that reads the same in three contexts is more valuable than three diffs that are each locally optimal.

Benchmarks and a generative fuzzer for the algorithms

The repository takes algorithm performance seriously enough to build its own tooling around it. The benches directory includes Criterion and allocation benchmarks, and the fuzz directory holds a generative performance fuzzer that isolates every diff algorithm, captures stacks on timeouts, and saves reproducible input pairs.

The design of that fuzzer is the interesting part. Isolating each algorithm means a timeout points at one implementation rather than at a pipeline. Capturing stacks on a timeout turns a hang into a diagnosis, since you can see where the algorithm was spending its time. And saving the input pairs makes a failure reproducible, which is the difference between a bug report someone else can act on and a story.

The Makefile exposes the same workflow as commands. The bench target runs the diffs benchmark, bench-memory runs the allocations benchmark, and perf-fuzz runs the perf-fuzz example in release mode with arguments passed through a PERF_FUZZ_ARGS variable.

code
perf-fuzz:
	@cargo run --release --example perf-fuzz -- $(PERF_FUZZ_ARGS)

The two benchmark flavours matter for different reasons. The time benchmark answers which algorithm is fastest on your kind of input, and the allocation benchmark answers something the time benchmark hides, which is how much memory each algorithm churns through to get there. A diff library that is fast because it allocates heavily behaves differently under memory pressure, and the project measures both.

Apache-2.0, no tagged releases, and a documented 2.7 to 3.0 upgrade

The licence is Apache-2.0, with the LICENSE file in the repository root and the manifest declaring the same. The authors listed in the manifest are Armin Ronacher, Pierre-Étienne Meunier and Brandon Williams, and the crate is published on crates.io as similar with the documentation on docs.rs.

The current version is 3.2.0, the edition is 2024 and the rust-version is 1.85, so a current toolchain is a hard requirement. The repository has no GitHub releases, which is normal for a crate published through crates.io, but it does mean the changelog and the crates.io version history are the record of what changed.

The one migration to plan for is documented rather than left to discovery. There is an Upgrading Guide for the jump from 2.7 to 3.0, kept as UPGRADING.md at the top level, and the manifest's include list shows the published crate carries the changelog, the manifest, the licence, the readme and the source, which is a deliberately small package. A user upgrading from the 2.x line should read that file first, because the default feature set changed and std is now on by default.

For the surrounding ecosystem, the README names the related projects: insta for snapshot testing, similar-asserts for assertions, and similar-rs for Python bindings. The last of those is the interesting one for a mixed stack, since it means the same diffing behaviour can be reached from Python if you need the output to match across two services.

Editorial conclusion

Adopt similar when you need diff output in Rust without pulling in a dependency tree, since the crate ships no required dependencies and exposes heuristic and raw Myers, patience, Hunt, histogram and classic LCS diffs alongside unified output and three way merging. Do not adopt it if you need diffing at the AST or semantic level, because everything here operates on sequences of comparable items rather than on meaning. Before you commit, decide which algorithm suits your data, because patience diffs tend to read better on source code while the heuristic Myers default is a general purpose choice, and read UPGRADING.md if you are moving from the 2.7 line to 3.0.

Frequently asked questions

Which diffing algorithms does similar implement?

A practical heuristic Myers diff and a raw Myers diff, patience diff, a Hunt-style diff, a histogram diff, and a classic LCS table diff. Diffing is also available on arbitrary comparable sequences and at line, word, character and grapheme level, over text and over bytes.

Can I use similar without the standard library?

Yes. Add `similar = { version = "3", default-features = false }` and you get the no_std plus alloc build, where the backend map is alloc::collections::BTreeMap by default or hashbrown::HashMap when the hashbrown feature is enabled.

Does similar produce unified diffs and three way merges?

Yes. The crate lists unified diff generation and structured three-way line merging with merge and diff3 conflict output among its features, and the examples directory contains a udiff.rs example for the unified format.

Does similar depend on other crates?

The default configuration is dependency free, with std and text as the only default features. The optional dependencies are unicode-segmentation, bstr, serde with alloc and derive, web-time and hashbrown, each pulled in by a specific feature flag.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. mitsuhiko/similar on GitHub
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mitsuhiko-similar.svg)](https://hysenlabs.com/projects/mitsuhiko-similar)

Was this useful?