微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

如何解决如何在 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 特性,因此它失败了。

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

  • 更改方法签名以采用 mut self 代替。但这会将对象的所有权转移到方法中,之后就不能再使用了。

  • digest 字段包装在 RefMutCell 中,以尝试采用 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()

解决方法

无论如何,我宁愿不复制它。我宁愿拥有 [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()
    }
}

如何避免 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's digest factory answer,这是使用内部可变性的替代实现:

<a href="https://www.google.com" aria-label="Some title Some description">
  <span class="title" aria-hidden="true">Some title</span>
  <span class="description" aria-hidden="true">Some description</span>
</a>

playground

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。