stringgenericsrustformattingsha2

Format hash value to hex-string when using generics for different sha2-hash methods



I'm currently working with the 'sha2' library.
One of the functions in my project is supposed to calculate the hash of a file using different methods (Sha224, Sha256, Sha384, Sha512).
All of the hash methods use the trait sha2::Digest and all return a GenericArray<u8, Self::OutputSize>.
However, since a generic type is used, i get following error message:

cannot add `<T as OutputSizeUser>::OutputSize` to `<T as OutputSizeUser>::OutputSize`

with this code:

use sha2::{Digest, Sha224, Sha256, Sha384, Sha512};
use std::error::Error;
use std::{env, fs, io, process};

//...

impl AutoSha {

//...

    fn calc_hash<T: Digest + io::Write>(&self, file_path: String) -> Result<String, Box<dyn Error>> {
        let mut hasher = T::new();
        let mut file = fs::File::open(file_path)?;

        io::copy(&mut file, &mut hasher)?;
        let hash = hasher.finalize();

        Ok(format!("{:x}", hash)) // This results in the error message
    }
}

//Call example:
let auto_sha: AutoSha = AutoSha::new();
let hash = auto_sha.calc_hash::<Sha256>("path/to/file".to_string());

Using, for example, Sha256::new() instead of T::new() works fine. I have tried to set both 'OutputSizeUser' and 'OutputSize' as trait bounds but was unable to find what namespace they are located in. According to the documentation it is supposedly part of 'crypto_common' which should be included in the project as it is a dependency of 'sha2', but i was not able to include it.
Additionally, i do not know whether that would solve my issue either.

How can i format the return value of 'hasher.finalize()' to a hexadecimal string?

Full error log

error[E0277]: cannot add `<T as OutputSizeUser>::OutputSize` to `<T as OutputSizeUser>::OutputSize`
   --> src/main.rs:80:28
    |
80  |         Ok(format!("{:x}", hash))
    |                            ^^^^ no implementation for `<T as OutputSizeUser>::OutputSize + <T as OutputSizeUser>::OutputSize`
    |
    = help: the trait `Add` is not implemented for `<T as OutputSizeUser>::OutputSize`
    = note: required because of the requirements on the impl of `LowerHex` for `GenericArray<u8, <T as OutputSizeUser>::OutputSize>`
note: required by a bound in `ArgumentV1::<'a>::new_lower_hex`
    = note: this error originates in the macro `$crate::__export::format_args` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider further restricting the associated type
    |
73  |     fn calc_hash<T: Digest + io::Write>(&self, file_path: String) -> Result<String, Box<dyn Error>> where <T as OutputSizeUser>::OutputSize: Add {
    |                                                                                                     ++++++++++++++++++++++++++++++++++++++++++++

For more information about this error, try `rustc --explain E0277`.
error: could not compile `hash-cmp` due to previous error

I have tried to use the recommended solution with std::ops::Add but did not manage to get it to work either.

Useful links


Solution

  • After experimenting/reading for a while i found following solution:

    fn calc_hash<T: Digest + io::Write>(&self, file_path: String) -> Result<String, Box<dyn Error>> {
        let mut hasher = T::new();
        let mut file = fs::File::open(file_path)?;
    
        io::copy(&mut file, &mut hasher)?;
        let hash = hasher.finalize();
    
        let mut hash_string = "".to_string();
        for byte in hash.iter() {
            hash_string = format!("{}{:02x}", hash_string, byte);
        }
        Ok(hash_string)
    }
    

    I'm basically just iterating over each byte of the GenericArray and then concat it with the end result.