Crates.io | portable-hash |
lib.rs | portable-hash |
version | 0.3.0 |
created_at | 2025-07-27 23:24:21.725397+00 |
updated_at | 2025-07-30 16:05:26.581823+00 |
description | Portable, stable hashing traits for Rust. |
homepage | |
repository | https://github.com/hoxxep/portable-hash |
max_upload_size | |
id | 1770498 |
size | 66,951 |
Note: not ready for production, yet!
Introducing PortableHash
and PortableHasher
: a set of traits for portable and stable hashing across different platforms and compiler versions. Stable, portable hashing made easy! This crate does not offer a hasher implementation, but provides the traits and macros to link data types implementing PortableHash
with hashers implementing PortableHasher
.
Sponsored by Upon, inheritance vaults for your digital life. Ensure your family can access your devices, accounts, and assets when the unexpected happens.
To use PortableHash
, simply derive or implement it on your types, and choose a PortableHasher
implementation that suits your needs.
By implementing PortableHash
on library types, you promise to guarantee that the type hashing logic is stable across:
OsString
, OsStr
, or Path
have platform-specific encodings and representations.std::hash::Hash
or other non-stable hashing traits to produce a PortableHash
output.PortableHash::portable_hash
must always hash the same fields in the same order for all crate minor versions.
derive(PortableHash)
. Changing the order of fields in structs or enums will change the hash output. We recommend writing unit tests that hash each of your types against hardcoded hash outputs to check for stability. Fields can be renamed safely, but cannot be re-ordered. Please implement PortableHash
manually to maintain stability if you need to change the order of fields.OsString
, OsStr
, and Path
are examples of types that vary between platforms. The string encodings of these types can differ based on the operating system, making them unsuitable for portable hashing. They can safely derive std::hash::Hash
for in-memory hashmaps, but PortableHash
is explicitly not implemented on these types.
use portable_hash::{PortableHash, PortableHasher, PortableHasherDigest};
use sha_hasher::Sha256Hasher;
#[derive(PortableHash, Default)]
struct MyType {
a: u32,
b: String,
}
let object = MyType { a: 42, b: "Hello".to_string() };
let mut hasher = Sha256Hasher::default();
object.portable_hash(&mut hasher);
assert_eq!(hasher.finish(), 5333351996764360352, "u64 output");
assert_eq!(hasher.digest(), [
160, 142, 66, 61, 98, 223, 3, 74, 108, 15, 1, 253, 229, 169, 86, 215,
117, 111, 201, 32, 16, 24, 16, 174, 206, 67, 25, 224, 226, 174, 4, 168
], "hasher-specific output type");
Hashers that implement PortableHasher
:
PortableHasher
.Implementing PortableHasher
is very similar to implementing the standard library Hasher
trait, with some additional requirements.
Your crate must provide the following guarantees when implementing PortableHasher
:
default-features = false
when adding portable-hash
as a dependency, so end users can opt to disable the std
feature.std::hash
traits?The standard library Hash
and Hasher
traits are not suitable for portable hashing across different platforms and versions of Rust. The hashing of primitive types, standard library types, implementation of derive(Hash)
, and the default behaviour of Hasher
methods may all change between platforms and compiler versions. This crate is intended to provide an equally easy to use alternative that is truly stable and portable across platforms and versions of Rust.
std::hash
traits in a stable and portable way?The default behaviour of hashing any primitive type, standard library type, and the default Hash
and Hasher
implementations are all subject to change between compiler versions.
Hash
is responsible for breaking down a type into primitive types to feed a Hasher
, while Hasher
is responsible for consuming those bytes and producing a hash output.
A Hasher
author must:
write_*
methods to ensure that compiler versions changing the default behaviour won't affect this Hasher
's output.And end users must:
Hasher
is portable, and promises to be stable between rust and crate versions.derive(Hash)
and implement Hash::hash
on their hashed types manually using Hasher::write_*
methods.Hash::hash
on types they haven't manually implemented, including primitive types like str
and tuples.Hasher::write_*
methods with default implementations (particularly the upcoming write_str
), which requires reading the Hasher implementation source code to check.write_usize
and write_isize
unless it is portably hashed across platforms by the Hasher
.This is so fraught with accidental footguns, PortableHash
and PortableHasher
have been provided to allow end users to simply derive(PortableHash)
and choose any PortableHasher
without worrying about the above pitfalls.
Do not use this crate in production yet as it's still under development. Please wait for the 1.0 release to stabilise the API and hash output. The PortableHash
and PortableHasher
traits deviate from the standard library in various ways that still need to be reviewed and documented, and are subject to change. Subscribe to notifications on the stabilisation issue to be notified of the 1.0 release. Issues and contributions are very welcome.
PortableHash
and PortableHasher
traits.PortableHash
on many primitive and standard library types.derive(PortableHash)
macro.derive(PortableHash)
macro to produce stable enum hashing. Re-ordering currently changes the hash output, while renaming is safe.PortableOrd
marker trait for collections that require stable ordering to hash portably, such as BTrees.HasherWrite
pattern?Hasher
trait methods.write_bytes
.write_usize
and write_isize
methods, as these types are not portable by default, unless we force them to always be write_u64
?finish
instead offer a custom Output type?PortableHasher
? Is the sha-hasher
a reasonable thing to publish?!
implementation, or remove the nightly feature.write_len_prefix
name change (differs from write_length_prefix
in the std library).write_str
default implementation change to use a length prefix.BuildPortableHasher
to PortableBuildHasher
?std
being a default feature or not.
default-features = false
so users can choose what to include.PortableHash
implementations for stability and DoS resistance, double-check the manual write_u8
enum discriminant keys.1.0 will mark the first stable release of portable-hash. Before then, consider this trait unstable.
Major version bumps will occur for:
PortableHash
implementation of a basic type.PortableHasher
method.Minor version bumps will occur for:
PortableHash
implementations.Users must be able to fix to a specific major version of portable-hash
. Any library with a portable-hash
dependency should make a major version bump of their crate if they change the major version of portable-hash
, unless their trait offers support for multiple versions of PortableHash
.