3

我想在 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将字段包装在RefMutorCell中,以采用内部可变性,但我无法找出正确的方法来借用可digest变性而不尝试复制值。此外,如果可能,希望在编译时保留借用检查。

  • 将 of 的类型更改为D返回 a 实例的函数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()复制并只返回由返回的数组finalize_reset()

4

2 回答 2

3

无论如何,我宁愿不复制它。我宁愿有一个 [of self.digest] 的实例。

问题是self.digest.chain() 消耗(拥有所有权)self.digestDigest::chain()这是您无法更改的合同的基本部分。内部可变性无济于事,因为它不是可变性问题,而是对象生命周期问题 - 在移动或丢弃对象后您无法使用它。

digest不过,您创建一个创建摘要的函数的想法应该可行。它将需要两种泛型类型,一种用于摘要类型,特征界限为Digest,另一种用于工厂,特征界限为Fn() -> D

struct Crypto<F> {
    digest_factory: F,
}

impl<D, F> Crypto<F>
where
    D: Digest,
    F: Fn() -> D,
{
    pub fn hash(&self, data: &[u8]) -> Vec<u8> {
        (self.digest_factory)()
            .chain(&data)
            .finalize()  // use finalize as the object is not reused
            .to_vec()
    }
}

如何避免to_vec()复制并只返回由返回的数组finalize_reset()

您可以hash()返回与 , 相同的finalize()类型digest::Output<D>

pub fn hash(&self, data: &[u8]) -> digest::Output<D> {
    (self.digest_factory)()
        .chain(&data)
        .finalize()
}
于 2021-01-18T23:41:51.090 回答
2

要添加到user4815162342 的摘要工厂答案,这里是使用内部可变性的替代实现:

use digest::Digest;
use std::cell::RefCell;

struct Crypto<D: Digest> {
    digest: RefCell<D>,
}

impl<D> Crypto<D>
where
    D: Digest,
{
    pub fn hash(&self, data: &[u8]) -> Vec<u8> {
        let mut digest = self.digest.borrow_mut();
        digest.update(&data);
        digest.finalize_reset().to_vec()
    }
}

操场

于 2021-01-18T23:52:18.343 回答