我对Rust很新,所以我的术语很混乱。
我想使用hashes包进一些哈希,我想动态选择在运行时使用哪种算法(sha256,sha512等)。
我想写这样的东西:
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
所需的关联类型。如果我试图填写它们:
"sha256" => Box::new(Sha256::new()) as Box<Digest<<OutputSize = U32, BlockSize = U64>>>,
我留下了一个错误:the trait 'digest::Digest' cannot be made into an object
。我认为这种方法无论如何都会失败,因为在不同算法具有不同关联类型的情况下,match
将返回稍微不同的类型。
我错过了一些明显的东西吗?如何动态创建实现特征的事物的实例,然后保持该事物并通过特征界面使用它?
答案 0 :(得分:2)
该消息引用object safety(longer article)。 Digest
trait有两个不兼容性:
Digest
个对象兼容的值来解决此问题。)fn result(self) -> …
)按值self
。你无法打电话给它,这会破坏这种特性的可用性。创建特征对象后,将删除有关其特定于子类型的功能(如内存布局或关联类型)的信息。对特征对象方法的所有调用都是通过vtable指针完成的。这意味着它们都必须兼容,并且Rust不允许您调用在这些方面可能有所不同的任何方法。
解决方法是创建与对象兼容的自定义包装器特征/适配器。我不确定这是否是最好的实施,但确实有效:
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();
}