如何对具有关联类型的特征进行装箱? [英] How to Box a trait that has associated types?
问题描述
我对Rust还是很陌生,所以我可能对术语感到困惑.
I'm very new to Rust so I may have terminology confused.
我想使用哈希板条箱进行一些哈希处理,并且我想动态选择哪种算法(sha256,sha512等)在运行时使用.
I want to use the hashes crates to do some hashing and I want to dynamically pick which algorithm (sha256, sha512, etc.) to use at runtime.
我想写这样的东西:
let hasher = match "one of the algorithms" {
"sha256" => Box::new(Sha256::new()) as Box<Digest>,
"sha512" => Box::new(Sha512::new()) as Box<Digest>
// etc...
};
由于没有指定Digest
所需的关联类型,我有点不起作用.如果我尝试填写它们:
I sort of get that that doesn't work because the associated types required by Digest
aren't specified. If I attempt to fill them in:
"sha256" => Box::new(Sha256::new()) as Box<Digest<<OutputSize = U32, BlockSize = U64>>>,
我离开了一个错误:the trait 'digest::Digest' cannot be made into an object
.我认为这种方法无论如何都会失败,因为在不同算法具有不同关联类型的情况下,match
返回的类型将有所不同.
I'm left with an error: the trait 'digest::Digest' cannot be made into an object
. I think this approach will fail anyway because match
will be returning slightly different types in cases where different algorithms have different associated types.
我缺少明显的东西吗?我该如何动态地创建实现特质的事物的实例,然后保留该事物并通过trait接口使用它?
Am I missing something obvious? How can I dynamically create an instance of something that implements a trait and then hold on to that thing and use it through the trait interface?
推荐答案
该消息引用了较长的文章). Digest
特征有两个不兼容之处:
The message refers to object safety (longer article). The Digest
trait has two incompatibilities:
- It uses associated types (this can be worked around by explicitly setting all type parameters to values compatible for all
Digest
objects). - It has a method (
fn result(self) -> …
) takingself
by value. You won't be able to call it, which ruins usability of this trait.
一旦创建了特征对象,就将删除有关其特定于子类型的功能(如内存布局或关联的类型)的信息.对trait对象的方法的所有调用都是通过vtable指针完成的.这意味着它们都必须兼容,Rust不允许您调用在这些方面可能有所不同的任何方法.
Once a trait object is created, information about its subtype-specific features such as memory layout or associated types is erased. All calls to the trait object's methods are done via a vtable pointer. This means they all must be compatible, and Rust can't allow you to call any methods that could vary in these aspects.
一种解决方法是创建与对象兼容的自定义包装特征/适配器.我不确定这是否是最好的实现,但是它确实有效:
A workaround is to create your custom wrapper trait/adapter that is object-compatible. I'm not sure if that's the best implementation, but it does work:
trait Digest {
type Assoc;
fn result(self);
}
struct Sha;
impl Digest for Sha {
type Assoc = u8;
fn result(self) {}
}
///////////////////////////////////////////
trait MyWrapper {
fn result(&mut self); // can't be self/Sized
}
impl<T: Digest> MyWrapper for Option<T> {
fn result(&mut self) {
// Option::take() gives owned from non-owned
self.take().unwrap().result()
}
}
fn main() {
let mut digest: Box<MyWrapper> = Box::new(Some(Sha));
digest.result();
}
这篇关于如何对具有关联类型的特征进行装箱?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!