如何在Rust中将内部可变性与泛型一起使用? [英] How can I use internal mutability with generic type in Rust?

查看:71
本文介绍了如何在Rust中将内部可变性与泛型一起使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在Rust中设计一个结构,该结构可以用实现 Digest 特性的对象构造,并抽象出方法背后的哈希行为.这是一个无法编译的简单示例:

I would like to design a struct in Rust that can be constructed with an object that implements the Digest trait, and abstract the behavior of the hash behind a method. Here's a simple example that doesn't compile:

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 特性,因此失败.

This fails to compile because self is immutably borrowed in the method signature, so self.digest cannot be immutably borrowed. So it tries to copy it, instead, but since the D generic is not defined to adhere to the Copy trait, it fails.

我还是不想复制它.我宁愿有一个实例.我尝试过的一些事情:

I'd rather not copy it, anyway. I'd rather have the one instance. Some things I've tried:

  • 更改方法签名以改为使用 mut self .但这会将对象的所有权移到方法中,之后无法再次使用它.

  • Changing the method signature to take mut self instead. But that moves ownership of the object into the method, after which it cannot be used again.

RefMut Cell 中包装 digest 字段,以采用

Wrapping the digest field in a RefMut or Cell, in an effort to adopt internal mutability, but I was not able to figure out the right method to then borrow the digest mutably without it trying to copy the value. Also, would prefer to keep borrow checks at compile-time if possible.

D 的类型更改为一个返回 Digest 实例的函数,并使用它实例化 hash内部的新摘要()方法.但是然后,即使我将其定义为 D:Box< dyn Digest> ,编译器也会抱怨必须指定关联类型OutputSize的值(来自特征摘要:: Digest).由于我想支持不同的散列算法,这些算法会产生大小不同的散列,因此这似乎具有挑战性.

Change the type of D to a function that returns an instance of a Digest, and use it to instantiate a new digest inside the hash() method. But then, even if I define it as D: Box<dyn Digest>, the compiler complains that the value of the associated type OutputSize (from trait digest::Digest) must be specified. So that seems challenging, since I want to support different hashing algorithms that will produce hashes of varying sizes.

我试图使用泛型来获得特质范围的编译时收益,但是不得不承认,与行为需要可变性的对象组成时,内部可变性的挑战使我感到沮丧.十分感谢针对这种设计挑战的惯用Rust解决方案的指针.

I was trying to use generics to get the compile-time benefits of trait bounds, but have to admit that the challenges of internal mutability when composing with objects whose behavior require mutability is thwarting me. Pointers to idiomatic Rust solutions to this design challenge greatly appreciated.

奖金-如何避免复制 to_vec()并仅返回

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

推荐答案

我还是不想复制它.我宁愿有一个[code.self.digest ]的实例.

问题在于 self.digest.chain() 消耗(拥有) self.digest 的所有权,这是一个基本部分不能更改的 Digest :: chain()合约的期限.内部可变性将无济于事,因为这不是可变性问题,而是对象生存期问题-移动或放置对象后,您将无法使用它.

The problem is that self.digest.chain() consumes (takes ownership of) self.digest, and that's a fundamental part of the contract of Digest::chain() which you cannot change. Interior mutability won't help because it's not a mutability issue, it's an object lifetime issue - you cannot use an object after it is moved or dropped.

您的使 digest 成为创建摘要的函数的想法应该起作用.它将需要两种通用类型,一种是摘要类型,特征绑定为 Digest ,另一种类型是工厂,特征绑定为 Fn()->.D :

Your idea to make digest a function that creates digests should work, though. It will require two generic types, one for the digest type, with a trait bound of Digest, and the other for the factory, with a trait bound of 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()
}

这篇关于如何在Rust中将内部可变性与泛型一起使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆