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::Add对OutputSize的GenericArray<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从实现者之一重新导出似乎更有意义。如果有反对的理由,请随时发表评论。
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)
由于这可以直接表达我们在通用函数中使用的内容,因此这似乎更可取。