I have a little bit of trouble wrapping my head around this problem. I am trying to write a generic function which can take any digest::Digest
and spits out a string form of the computed digest ("hex string").
Here is the non-generic version as minimal example:
#![forbid(unsafe_code)]
#![forbid(warnings)]
extern crate sha2; // 0.9.1
use sha2::{Sha256, Digest}; // 0.9.1
fn main() {
let hash = Sha256::new().chain("String data").finalize();
let s = format!("{:x}", hash);
println!("Result: {}", s);
}
... and here is my attempt at a generic version:
#![forbid(unsafe_code)]
#![forbid(warnings)]
extern crate sha2; // 0.9.1
extern crate digest; // 0.9.0
use digest::Digest;
use sha2::Sha256;
fn compute_hash<D: Digest>(input_data: &str) -> String {
let mut hasher = D::new();
hasher.update(input_data.as_bytes());
let digest = hasher.finalize();
format!("{:x}", digest)
}
fn main() {
let s = compute_hash::<Sha256>("String data");
println!("Result: {}", s);
}
... which gives the following error:
Compiling playground v0.0.1 (/playground)
error[E0277]: cannot add `<D as sha2::Digest>::OutputSize` to `<D as sha2::Digest>::OutputSize`
--> src/lib.rs:13:21
|
13 | format!("{:x}", digest)
| ^^^^^^ no implementation for `<D as sha2::Digest>::OutputSize + <D as sha2::Digest>::OutputSize`
|
= help: the trait `std::ops::Add` is not implemented for `<D as sha2::Digest>::OutputSize`
= note: required because of the requirements on the impl of `std::fmt::LowerHex` for `digest::generic_array::GenericArray<u8, <D as sha2::Digest>::OutputSize>`
= note: required by `std::fmt::LowerHex::fmt`
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider further restricting the associated type
|
9 | fn compute_hash<D: Digest>(input_data: &str) -> String where <D as sha2::Digest>::OutputSize: std::ops::Add {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground`.
Now suppose I understand the error correctly, the implementation of std::fmt::LowerHex
used by format!()
appears to require std::ops::Add
for the OutputSize
of the GenericArray<u8, N>
(i.e. the N
) which is returned by .finalize()
. However, the non-generic example suggests that there is such an implementation for ArrayLength<u8>
.
So, given I cannot implement the std::ops::Add
trait for an external type, how can I satisfy the compiler in this case?
Or maybe to rephrase my question, although I - being new with Rust - am not a 100% sure it is what I want: how do I tell the compiler to treat <D as sha2::Digest>::OutputSize
the same as ArrayLength<u8>
?
NB: I am relatively new with Rust, so please keep that in mind and kindly refer to the exact piece of documentation applicable to my case, rather than "the documentation" in general. I have scoured the documentation for digest
, for various of the implementers of digest::Digest
, for this error and for (what I thought were) similar issues and the traits topics in the Rust Book (2018 edition) for nearly three hours before I asked. Thanks.
In the second example I am using use digest::Digest;
. That's because in the future other hash algorithms are supposed to follow and it seemed to make more sense to use digest::Digest
directly instead of the re-exported Digest
from one of the implementers. If there is a reason against that, feel free to remark on it.
digest
crate usesgeneric_array
to work around the lack of language support for const generics. It seems likely that a future version of the language could allowdigest
to support this usage in a more direct way. – Prostitution