如何在 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
特性,因此它失败了。
无论如何,我不想复制它。我宁愿有一个实例。我尝试过的一些事情:
更改方法签名以改为采用 mut self
。但这会将对象的所有权转移到方法中,之后它就不能再使用了。
将 digest
字段包装在 RefMut
或 Cell
中,以努力采用 internal mutability,但我无法理解找出正确的方法,然后可变地借用 digest
而无需尝试复制该值。另外,如果可能的话,希望在编译时保留借用检查。
将 D
的类型更改为 return 是 Digest
实例的函数,并使用它在 [= =23=]方法。但是,即使我将其定义为 D: Box<dyn Digest>
,编译器也会抱怨 the value of the associated type OutputSize (from trait digest::Digest) must be specified
。所以这看起来很有挑战性,因为我想支持不同的哈希算法,这些算法会产生不同大小的哈希值。
我试图使用泛型来获得特征边界的编译时优势,但不得不承认,在与行为需要可变性的对象组合时,内部可变性的挑战阻碍了我。非常感谢针对此设计挑战的惯用 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()
}
}
我想用 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
字段包装在RefMut
或Cell
中,以努力采用 internal mutability,但我无法理解找出正确的方法,然后可变地借用digest
而无需尝试复制该值。另外,如果可能的话,希望在编译时保留借用检查。将
D
的类型更改为 return 是Digest
实例的函数,并使用它在 [= =23=]方法。但是,即使我将其定义为D: Box<dyn Digest>
,编译器也会抱怨the value of the associated type OutputSize (from trait digest::Digest) must be specified
。所以这看起来很有挑战性,因为我想支持不同的哈希算法,这些算法会产生不同大小的哈希值。
我试图使用泛型来获得特征边界的编译时优势,但不得不承认,在与行为需要可变性的对象组合时,内部可变性的挑战阻碍了我。非常感谢针对此设计挑战的惯用 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 byfinalize_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()
}
}