Openssl sha256 DigestBytes to [u8;32]

I have an implementation that puts hashes into a tree structure.
Currently the hashes are Vec<u8> and I am using https://github.com/sfackler/rust-openssl to compute the hashes. Which is easy as rust-openssl has a to_vec method for DigestBytes.

I know that the hashes are always 32 bytes long because they are sha256 hashes and I thought that maybe there is some speed advantage to put the DigestBytes into fixed sized [u8;32].

let digest_bytes: DigestBytes = hasher.finish()?;
let fixed_size_u8 : [u8; HASHBYTES] = digest_bytes.to_vec().iter().take(32).collect();

does not compile.

error[E0277]: a collection of type `[u8; 32]` cannot be built from an iterator over elements of type `&u8`
  --> src/utils/crypto/hash/openssl.rs:18:81
   |
18 |     let fixed_size_u8 : [u8; HASHBYTES] = digest_bytes.to_vec().iter().take(32).collect();
   |                                                                                 ^^^^^^^ a collection of type `[u8; 32]` cannot be built from `std::iter::Iterator<Item=&u8>`
   |
   = help: the trait `std::iter::FromIterator<&u8>` is not implemented for `[u8; 32]`

error[E0277]: a collection of type `[u8; 32]` cannot be built from an iterator over elements of type `&u8`

My questions

  1. Is it worth the effort to let the compiler know the size?
  2. Why doesn't have rust-openssl have a FixedOutput that has a compile-time fixed size of 32 for sha32
  3. How do I copy efficiently copy the DigestBytes bytes to a [u8;32];
  4. into_boxed_slice looks interesting because I think I have to put the hashes in Boxes anyway, but then again how do I turn this into a 32-byte length slice

Thoughts?

  1. Working with arrays is quite painful right now. Hopefully things will improve with the introduction of const generics.
  2. Probably because of point 1.
  3. The best way is this:
let mut a = [0u8; 32];
a.copy_from_slice(&b);

And then you hope the compiler optimizes away at least the array initialization, and sometimes the copy if possible.

  1. A boxed slice doesn't have a fixed compile-time length. It's basically a Vec but the length can't change automatically through push/pop etc.

You can just use https://doc.rust-lang.org/nightly/std/convert/trait.TryFrom.html#impl-TryFrom<%26'_%20[T]>, because DigestBytes derefs to &[u8] https://docs.rs/openssl/0.10.24/openssl/hash/struct.DigestBytes.html#impl-Deref.

So something like

let digest_bytes: DigestBytes = hasher.finish()?;
let fixed_size_u8 : [u8; HASHBYTES] = digest_bytes.try_into().unwrap();

should work.

Sadly, it doesn't

error[E0277]: the trait bound `[u8; 32]: std::convert::From<utils::crypto::hash::openssl::hash::DigestBytes>` is not satisfied
  --> src/utils/crypto/hash/openssl.rs:19:56
   |
19 |     let fixed_size_u8 : [u8; HASHBYTES] = digest_bytes.try_into().unwrap();
   |                                                        ^^^^^^^^ the trait `std::convert::From<utils::crypto::hash::openssl::hash::DigestBytes>` is not implemented for `[u8; 32]`
   |
   = note: required because of the requirements on the impl of `std::convert::Into<[u8; 32]>` for `utils::crypto::hash::openssl::hash::DigestBytes`
   = note: required because of the requirements on the impl of `std::convert::TryFrom<utils::crypto::hash::openssl::hash::DigestBytes>` for `[u8; 32]`
   = note: required because of the requirements on the impl of `std::convert::TryInto<[u8; 32]>` for `utils::crypto::hash::openssl::hash::DigestBytes`

This compiles:

let digest_bytes: DigestBytes = hasher.finish()?;
let mut fixed_size_u8 = [0; HASHBYTES];
fixed_size_u8.copy_from_slice(digest_bytes.as_ref());

Thank you

Oh, right, generics and deref don't mix. Try this instead:

let fixed_size_u8 : [u8; HASHBYTES] = digest_bytes[..].try_into().unwrap();