Brian: yes, at the moment `#[deriving(Hash)]` does support more than
SipHash. However this PR temporarily removes that feature to replace
`#[allow(default_type_param_usage)]` with `#[feature(default_type_params)]`:

https://github.com/mozilla/rust/pull/12525

If we land that change, we could try two things. First, we could just turn
on default_type_params permanently. Second, we could allow a syntax
extension to check if a feature has been enabled in a crate. Brian, I know
you were talking about this earlier on IRC, did you end up making a
decision on what to do?

György: Yep, there is. I went through approximately 13 iterations to
converge on this design :) The reason why the `impl`'s need to know what
kind of state they are dealing with is to support custom hashers. In most
cases, people will want to compute a hash over the bytes of a value, so
that value's hash impl needs to know that it can pass the value's bytes in
with `state.write(...)`. Other values may actually have a hash baked into
them, or want to use an integer value as a hash. These can just return the
hash value by directly assigning the hash to the `*state` as I did in the
example. While not  cryptographically safe, this can be dramatically faster
than using something like SipHash. Yet another value may want to use two
completely different hashing algorithms depending depending on some
property. Letting the `Hash`ee know about the state type allows for this
level of flexibility. It also adds some type safety, in that it'll prevent
you from accidentally passing a `Writer`-based hash state into a type that
should only be used with a specific hasher.



On Mon, Feb 24, 2014 at 9:35 PM, Niko Matsakis <[email protected]> wrote:

> This looks very cool.
>
>
> On Mon, Feb 24, 2014 at 11:31:19AM -0500, Erick Tryzelaar wrote:
> > I'm happy to announce that Rust's new hashing framework has landed in:
> >
> > https://github.com/mozilla/rust/pull/11863
> > https://github.com/mozilla/rust/pull/12492
> >
> > This PR has has changed how to declare a type is hashable. Here's a full
> > example on how to hash a value using either the new `#[deriving(Hash)]`
> or
> > manual implementation.
> >
> > ```
> > use std::hash::{Hash, hash};
> > use std::hash::sip::SipState;
> >
> > #[deriving(Hash)]
> > struct Foo {
> >     a: ~str,
> >     b: uint,
> >     c: bool,
> > }
> >
> > struct Bar {
> >     a: ~str,
> >     b: uint,
> >     c: bool,
> > }
> >
> > impl Hash for Bar {
> >     fn hash(&self, state: &mut SipState) {
> >         self.a.hash(state);
> >         self.b.hash(state);
> >         self.c.hash(state);
> >     }
> > }
> >
> > fn main() {
> >     let foo = Foo { a: ~"hello world", b: 5, c: true };
> >     println!("{}", hash(&foo));
> >
> >     let bar = Bar { a: ~"hello world", b: 5, c: true };
> >     println!("{}", hash(&bar));
> > }
> > ```
> >
> > We also have experimental support for hashers that compute a value off a
> > stream of bytes:
> >
> > ```
> > use std::hash::{Hash, Hasher};
> > use std::io::IoResult;
> >
> > #[deriving(Hash)] // automatically provides hashing from a stream of
> bytes
> > struct Foo {
> >     a: ~str,
> >     b: uint,
> >     c: bool,
> > }
> >
> > struct Bar {
> >     a: ~str,
> >     b: uint,
> >     c: bool,
> > }
> >
> > #[allow(default_type_param_usage)]
> > impl<S: Writer> Hash<S> for Bar {
> >     fn hash(&self, state: &mut S) {
> >         self.a.hash(state);
> >         self.b.hash(state);
> >         self.c.hash(state);
> >     }
> > }
> >
> > struct SumState {
> >     sum: u64,
> > }
> >
> > impl Writer for SumState {
> >     fn write(&mut self, bytes: &[u8]) -> IoResult<()> {
> >         for byte in bytes.iter() {
> >             self.sum += *byte as u64;
> >         }
> >         Ok(())
> >     }
> > }
> >
> > struct SumHasher;
> >
> > #[allow(default_type_param_usage)]
> > impl Hasher<SumState> for SumHasher {
> >     fn hash<T: Hash<SumState>>(&self, value: &T) -> u64 {
> >         let mut state = SumState { sum: 0 };
> >         value.hash(&mut state);
> >         state.sum
> >     }
> > }
> >
> > fn main() {
> >     let hasher = SumHasher;
> >     let foo = Foo { a: ~"hello world", b: 5, c: true };
> >     println!("{}", hasher.hash(&foo));
> >     let bar = Bar { a: ~"hello world", b: 5, c: true };
> >     println!("{}", hasher.hash(&bar));
> > }
> > ```
> >
> > Finally, we also support completely custom hash computation:
> >
> > ```
> > use std::hash::{Hash, Hasher};
> >
> > struct Foo {
> >     hash: u64
> > }
> >
> > #[allow(default_type_param_usage)]
> > impl Hash<u64> for Foo {
> >     fn hash(&self, state: &mut u64) {
> >         *state = self.hash
> >     }
> > }
> >
> > struct CustomHasher;
> >
> > #[allow(default_type_param_usage)]
> > impl Hasher<u64> for CustomHasher {
> >     fn hash<T: Hash<u64>>(&self, value: &T) -> u64 {
> >         let mut state = 0;
> >         value.hash(&mut state);
> >         state
> >     }
> > }
> >
> > fn main() {
> >     let hasher = CustomHasher;
> >     let foo = Foo { hash: 5 };
> >     println!("{}", hasher.hash(&foo));
> > }
> > ```
> >
> > This may break over the next couple days/weeks as we figure out the right
> > way to do this. Furthermore, HashMaps have not yet been updated to take
> > advantage of the custom hashers, but that should be coming later on this
> > week.
> >
> > I hope they work well for all of you. If you run into any trouble, please
> > file bugs and cc @erickt on the ticket.
> >
> > Thanks,
> > Erick
>
> > _______________________________________________
> > Rust-dev mailing list
> > [email protected]
> > https://mail.mozilla.org/listinfo/rust-dev
>
>
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to