Trait std::hash::Hash

pub trait Hash {
    /// Returns `true` if the *in-memory representation* of the type is
    /// byte-for-byte identical to the *hash bytes representation* that
    /// [Hash::hash] writes into the [Hasher]; otherwise, returns `false`.
    ///
    /// # Additional Information
    ///
    /// When a type is trivially hashable, its hash can be computed by using
    /// the raw memory of the value (`__size_of::<Self>()` bytes starting at the
    /// value's address), without first building an intermediate byte buffer
    /// in a [Hasher]. This is what the [sha256] and [keccak256] functions do
    /// for trivially hashable types, which is significantly more gas effective.
    ///
    /// **Returning `true` is a strong guarantee: an incorrect `true` will produce
    /// wrong hashes when a value is hashed via [sha256] or [keccak256].** When in
    /// doubt, return `false`. Returning `false` is always safe; it only forgoes
    /// the optimization.
    ///
    /// # Warning
    ///
    /// Several subtleties make types that might look trivially hashable actually
    /// **not** trivially hashable. In particular:
    ///
    /// - `u16` and `u32` are stored in memory in an eight-byte slot (as `u64`),
    ///   but their hash bytes representation is only two and four bytes,
    ///   respectively. They are therefore **never** trivially hashable, and
    ///   neither is any aggregate (struct, tuple, array, ...) containing them.
    /// - `bool`, `u8`, `u16`, and `u32` fields inside a struct or tuple are
    ///   padded to eight bytes in memory. An aggregate containing such a field
    ///   is therefore **not** trivially hashable, even though the field types
    ///   themselves might be when hashed on their own.
    /// - Enum tags are stored in memory as `u64`, but the `Hash` implementations
    ///   in this library hash them as `u8`. Enums are therefore **not** trivially
    ///   hashable.
    /// - Collections (`Bytes`, `Vec`, `raw_slice`, `str`, `str[N]`, arrays, and
    ///   aggregates containing them) can be trivially hashable or not depending
    ///   on the `new_hashing` experimental feature. When `new_hashing` is
    ///   enabled, collections prefix their content with their length, which means
    ///   their hash bytes representation no longer matches their in-memory
    ///   representation, making them **not** trivially hashable.
    ///   For more details see: https://github.com/FuelLabs/sway/issues/7256.
    // TODO-MEMLAY: In this doc-comment we make an assumption about the memory
    //              layouts. Those can be changed in the future.
    // TODO: (TRIVIALLY-HASHABLE-ENUMS) Once we change enum implementations, adapt
    //       the doc-comment.
    fn is_hash_trivial() -> bool;
    /// Writes the hash bytes representation of `self` into the [Hasher] `state`.
    fn hash(self, ref mut state: Hasher);
}
Expand description

Trait for hashing values into a [Hasher].

Types implementing Hash can be hashed using the [Hasher], or via the
[sha256] and [keccak256] convenience functions, and can be used in places
that require a deterministic hash, e.g., as keys in a StorageMap.

Implementing Hash

A Hash implementation defines how the value is written into the [Hasher]
via the [Hash::hash] method, and declares, via [Hash::is_hash_trivial],
whether the value’s in-memory representation is byte-for-byte identical to
the bytes it writes into the [Hasher].

use std::hash::{Hash, Hasher};

struct MyU64Wrapper {
    value: u64,
}

impl Hash for MyU64Wrapper {
    fn is_hash_trivial() -> bool {
        // A struct with a single `u64` field has the same in-memory
        // representation as its hash bytes representation.
        true
    }

    fn hash(self, ref mut state: Hasher) {
        self.value.hash(state);
    }
}

Required Methods

Returns true if the in-memory representation of the type is
byte-for-byte identical to the hash bytes representation that
[Hash::hash] writes into the [Hasher]; otherwise, returns false.

Additional Information

When a type is trivially hashable, its hash can be computed by using
the raw memory of the value (__size_of::<Self>() bytes starting at the
value’s address), without first building an intermediate byte buffer
in a [Hasher]. This is what the [sha256] and [keccak256] functions do
for trivially hashable types, which is significantly more gas effective.

Returning true is a strong guarantee: an incorrect true will produce
wrong hashes when a value is hashed via [sha256] or [keccak256].
When in
doubt, return false. Returning false is always safe; it only forgoes
the optimization.

Warning

Several subtleties make types that might look trivially hashable actually
not trivially hashable. In particular:

  • u16 and u32 are stored in memory in an eight-byte slot (as u64),
    but their hash bytes representation is only two and four bytes,
    respectively. They are therefore never trivially hashable, and
    neither is any aggregate (struct, tuple, array, …) containing them.
  • bool, u8, u16, and u32 fields inside a struct or tuple are
    padded to eight bytes in memory. An aggregate containing such a field
    is therefore not trivially hashable, even though the field types
    themselves might be when hashed on their own.
  • Enum tags are stored in memory as u64, but the Hash implementations
    in this library hash them as u8. Enums are therefore not trivially
    hashable.
  • Collections (Bytes, Vec, raw_slice, str, str[N], arrays, and
    aggregates containing them) can be trivially hashable or not depending
    on the new_hashing experimental feature. When new_hashing is
    enabled, collections prefix their content with their length, which means
    their hash bytes representation no longer matches their in-memory
    representation, making them not trivially hashable.
    For more details see: https://github.com/FuelLabs/sway/issues/7256.

Writes the hash bytes representation of self into the [Hasher] state.