计算散列(digest::Digest trait)并返回字符串的通用函数

0xC*_*22L 5 generics traits rust

我在解决这个问题时遇到了一些麻烦。我正在尝试编写一个通用函数,它可以接受任何digest::Digest并吐出计算摘要的字符串形式(“十六进制字符串”)。

这是作为最小示例的非通用版本

#![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);
}
Run Code Online (Sandbox Code Playgroud)

...这是我对通用版本的尝试:

#![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);
}
Run Code Online (Sandbox Code Playgroud)

...这给出了以下错误:

   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`.
Run Code Online (Sandbox Code Playgroud)

现在假设我理解正确的错误,执行std::fmt::LowerHex使用format!() 出现需要std::ops::AddOutputSizeGenericArray<u8, N>(即N),这是由归国.finalize()。但是,非通用示例表明ArrayLength<u8>.

因此,鉴于我无法std::ops::Add为外部类型实现trait,在这种情况下我如何满足编译器?

或者重新表述我的问题,尽管我 - 是 Rust 的新手 - 并不能 100% 确定这是我想要的:我如何告诉编译器将其视为<D as sha2::Digest>::OutputSize相同ArrayLength<u8>

注意:我对 Rust 比较陌生,所以请记住这一点,并请参考适用于我的案例的确切文档,而不是一般的“文档”。在我提出要求之前digest,我已经搜索了、 的各种实现者的文档digest::Digest,针对此错误以及(我认为是)类似问题和 Rust Book(2018 版)中的特征主题。谢谢。


在第二个示例中,我使用use digest::Digest;. 那是因为将来应该遵循其他哈希算法,并且digest::Digest直接使用而不是Digest从实现者之一重新导出似乎更有意义。如果有反对的理由,请随时发表评论。

Jef*_*ett 7

Rust 要求您指定在泛型中使用的所有功能。笔记:

   |                     ^^^^^^ 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`
Run Code Online (Sandbox Code Playgroud)

试图说我们正在使用Add该类型D::OutputSize,但不需要它作为约束,我们可以这样做:

fn compute_hash<D: Digest>(input_data: &str) -> String
    where D::OutputSize: std::ops::Add
Run Code Online (Sandbox Code Playgroud)

如果您进行此更改,您将出现下一个错误:

   |                     ^^^^^^ the trait `digest::generic_array::ArrayLength<u8>` is not implemented for `<<D as sha2::Digest>::OutputSize as std::ops::Add>::Output`
Run Code Online (Sandbox Code Playgroud)

所以还有另一个要求,但我们也可以添加:

fn compute_hash<D: Digest>(input_data: &str) -> String
    where D::OutputSize: std::ops::Add,
          <D::OutputSize as std::ops::Add>::Output: digest::generic_array::ArrayLength<u8>
Run Code Online (Sandbox Code Playgroud)

这将编译。

但让我们深入探讨一下为什么这些限制是必要的。finalize返回Output<D>并且我们知道它是类型GenericArray<u8, <D as Digest>::OutputSize>。显然format!("{:x}", ...)需要该特征LowerHex,因此我们可以看到该类型何时满足该特征。

impl<T: ArrayLength<u8>> LowerHex for GenericArray<u8, T>
where
    T: Add<T>,
    <T as Add<T>>::Output: ArrayLength<u8>, 
Run Code Online (Sandbox Code Playgroud)

这看起来很熟悉。因此,如果这些约束为真,则的返回类型finalize满足。LowerHex

但我们可以更直接地了解同一件事。我们希望能够使用LowerHex以下格式进行格式化

fn compute_hash<D: Digest>(input_data: &str) -> String
    where digest::Output<D>: core::fmt::LowerHex
Run Code Online (Sandbox Code Playgroud)

由于这可以直接表达我们在通用函数中使用的内容,因此这似乎更可取。