繁体   English   中英

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

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

我想在 Rust 中设计一个结构,它可以用实现Digest特征的 object 构造,并在方法后面抽象 hash 的行为。 这是一个无法编译的简单示例:

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 但这会将 object 的所有权转移到方法中,之后就无法再次使用。

  • digest字段包装在RefMutCell中,以努力采用内部可变性,但我无法找到正确的方法,然后在不尝试复制值的情况下可变地借用digest 此外,如果可能,希望在编译时保留借用检查。

  • D的类型更改为返回Digest实例的 function ,并使用它在hash()方法中实例化新的摘要。 但是,即使我将其定义为D: Box<dyn Digest> ,编译器也会抱怨the value of the associated type OutputSize (from trait digest::Digest) must be specified 所以这看起来很有挑战性,因为我想支持不同的哈希算法,这些算法会产生不同大小的哈希值。

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

奖励 - 我如何避免to_vec()复制并只返回finalize_reset()返回的数组

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

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

不过,您创建digest的 function 的想法应该可行。 它将需要两种泛型类型,一种用于摘要类型,具有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()
}

要添加到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()
    }
}

操场

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM