I would like to design a struct in Rust that can be constructed with an object that implements the Digest
trait, and abstract the behavior of the hash behind a method. Here's a simple example that doesn't compile:
use digest::Digest;
struct Crypto<D: Digest> {
digest: D,
}
impl<D> Crypto<D>
where
D: Digest,
{
pub fn hash(&self, data: &[u8]) -> Vec<u8> {
self.digest.chain(&data).finalize_reset().to_vec()
}
}
This fails to compile because self
is immutably borrowed in the method signature, so self.digest
cannot be immutably borrowed. So it tries to copy it, instead, but since the D
generic is not defined to adhere to the Copy
trait, it fails.
I'd rather not copy it, anyway. I'd rather have the one instance. Some things I've tried:
Changing the method signature to take
mut self
instead. But that moves ownership of the object into the method, after which it cannot be used again.Wrapping the
digest
field in aRefMut
orCell
, in an effort to adopt internal mutability, but I was not able to figure out the right method to then borrow thedigest
mutably without it trying to copy the value. Also, would prefer to keep borrow checks at compile-time if possible.Change the type of
D
to a function that returns an instance of aDigest
, and use it to instantiate a new digest inside thehash()
method. But then, even if I define it asD: Box<dyn Digest>
, the compiler complains thatthe value of the associated type OutputSize (from trait digest::Digest) must be specified
. So that seems challenging, since I want to support different hashing algorithms that will produce hashes of varying sizes.
I was trying to use generics to get the compile-time benefits of trait bounds, but have to admit that the challenges of internal mutability when composing with objects whose behavior require mutability is thwarting me. Pointers to idiomatic Rust solutions to this design challenge greatly appreciated.
Bonus — how do I avoid the to_vec()
copy and just return the array returned by finalize_reset()
?
chain
requires you to movedigest
, so what do you plan on doing to replace the olddigest
with? – Aplet123chain
. Butself.digest.update(&data); self.digest.finalize_reset().to_vec()
still wants to borrowdigest
as immutable, and can't. – theorychain
function you can updatehash
's method signature to take&mut self
instead of&self
and that seems to meet all of your requirements, no? – pretzelhammerchain
wanted to movedigest
, so removing that and changing the signature tomut &self
does indeed fix it, as long as I also create the Crypto object as mutable. Would be nice to keep it internal, though. – theoryCrypto
instances remain immutable or... you want people to be able to callhash
even on an immutableCrypto
? – pretzelhammer