【发布时间】:2021-04-23 06:37:15
【问题描述】:
我想在 Rust 中设计一个结构,它可以用实现 Digest 特征的对象构造,并抽象出方法背后的哈希行为。这是一个无法编译的简单示例:
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()
}
}
这无法编译,因为self 在方法签名中被不可变地借用,所以self.digest 不能被不可变地借用。因此,它尝试复制它,但由于 D 泛型未定义为遵循 Copy 特征,因此它失败了。
无论如何,我宁愿不复制它。我宁愿有一个实例。我尝试过的一些事情:
-
将方法签名改为采用
mut self。但这会将对象的所有权转移到方法中,之后就不能再使用了。 -
将
digest字段包装在RefMut或Cell中,以努力采用internal mutability,但我无法找到正确的方法,然后在没有尝试的情况下可变地借用digest复制值。此外,如果可能,希望在编译时保留借用检查。 -
将
D的类型更改为返回Digest实例的函数,并使用它在hash()方法内实例化一个新摘要。但是,即使我将其定义为D: Box<dyn Digest>,编译器也会抱怨the value of the associated type OutputSize (from trait digest::Digest) must be specified。所以这看起来很有挑战性,因为我想支持不同的哈希算法,这些算法会产生不同大小的哈希值。
我试图使用泛型来获得 trait bound 的编译时优势,但不得不承认,在与行为需要可变性的对象组合时,内部可变性的挑战阻碍了我。非常感谢针对这一设计挑战的惯用 Rust 解决方案的指针。
奖励 — 我如何避免 to_vec() 复制而只返回 the array returned by finalize_reset()?
【问题讨论】:
-
chain要求你移动digest,那么你打算用什么来替换旧的digest? -
我可以取消
chain。但是self.digest.update(&data); self.digest.finalize_reset().to_vec()还是想借用digest作为不可变的,不能。 -
去掉
chain函数后,您可以更新hash的方法签名以采用&mut self而不是&self,这似乎满足您的所有要求,不是吗? -
啊,是的,我没有意识到
chain想要移动digest,所以删除它 并 将签名更改为mut &self确实可以解决它,只要我还将 Crypto 对象创建为可变的。不过,将其保留在内部会很好。 -
@theory 您能否澄清一下“将其保留在内部”是什么意思?是所有
Crypto实例保持不可变的强烈要求,还是......您希望人们即使在不可变的Crypto上也能够调用hash?
标签: generics rust traits composition interior-mutability