harsh
A Rust implementation of Hashids for short, reversible IDs from numbers.
Repository Health
Technical Analysis
Harsh is a Rust implementation of the Hashids algorithm: it encodes one or more numbers into a short, unique, YouTube-like string and decodes it back to the original numbers. It is the tool to reach for when you want to expose opaque identifiers in URLs or APIs instead of leaking sequential database ids.
The encoding is reversible and deterministic, not cryptographic — it obfuscates rather than encrypts. Harsh gives you a small builder API to customize the salt (making ids unique to your project), a minimum id length via padding, and a custom alphabet, so the generated ids fit your product’s look while staying decodable.
What You Get
encodeanddecodefor turning number arrays into short strings and back- A builder to set a salt so ids are unique to your project
- Minimum-length padding to make ids at least a chosen length
- Custom alphabet support to control which characters appear
- Hex-string encoding and decoding helpers
Common Use Cases
- Hiding sequential database ids behind opaque public identifiers
- Generating short, URL-friendly ids for links and API resources
- Producing shareable codes that decode back to structured numbers
- Creating project-scoped ids that differ across environments via salt
Under The Hood
Architecture — The crate is three small modules. src/lib.rs re-exports the public surface, src/builder.rs implements a HarshBuilder that validates configuration (salt, minimum length, alphabet, separators) and produces a Harsh instance, and src/harsh.rs holds the core algorithm — the alphabet shuffling, separator/guard handling, and the encode/decode/encode_hex/decode_hex methods. Construction does the expensive setup once so per-call encoding is cheap.
Tech Stack — Plain Rust (edition 2018) with zero runtime dependencies; criterion is used only as a dev-dependency for the benchmarks under benches/. The crate is categorized for encoding, value-formatting, and web-programming use.
Code Quality — For a ~1,000-line crate it is well tested: tests/ cover round-trip encode/decode against the reference Hashids vectors, and examples/ plus benches/ document real usage. Error cases (invalid alphabet, decode of malformed input) return Result, so failure modes are explicit rather than panicking.
API Design — Ergonomics are strong: Harsh::default() works out of the box, and the fluent builder (Harsh::builder().salt("...").length(10).build()) reads clearly for customization. encode takes a slice of u64 and decode returns a Result<Vec<u64>>, so the reversible contract is obvious from the signatures. The concepts are simple enough that the learning curve is minimal.