如何在 Rust 中使用泛型类型的内部可变性?

How can I use internal mutability with generic type in Rust?

我想用 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 特性,因此它失败了。

无论如何,我不想复制它。我宁愿有一个实例。我尝试过的一些事情:

我试图使用泛型来获得特征边界的编译时优势,但不得不承认,在与行为需要可变性的对象组合时,内部可变性的挑战阻碍了我。非常感谢针对此设计挑战的惯用 Rust 解决方案的指针。

奖金 — 我如何避免 to_vec() 复制而只是 return the array returned by finalize_reset()

I'd rather not copy it, anyway. I'd rather have the one instance [of self.digest].

问题是 self.digest.chain() 消耗 (拥有)self.digest,这是 Digest::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()
    }
}

how do I avoid the to_vec() copy and just return the array returned by finalize_reset()?

你可以有 hash() return 与 finalize() 相同的类型,digest::Output<D>:

pub fn hash(&self, data: &[u8]) -> digest::Output<D> {
    (self.digest_factory)()
        .chain(&data)
        .finalize()
}

要添加到 ,这里有一个使用内部可变性的替代实现:

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()
    }
}

playground