如何将具有关联类型的特征装箱?

Wes*_*tum 6 traits rust associated-types

我对 Rust 很陌生,所以我可能会混淆术语。

我想使用hashes crate 做一些散列,我想动态地选择在运行时使用哪种算法(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...
};
Run Code Online (Sandbox Code Playgroud)

我有点明白这是行不通的,因为Digest没有指定所需的关联类型。如果我尝试填写它们:

"sha256" => Box::new(Sha256::new()) as Box<Digest<<OutputSize = U32, BlockSize = U64>>>,
Run Code Online (Sandbox Code Playgroud)

我留下了一个错误:the trait 'digest::Digest' cannot be made into an object。我认为这种方法无论如何都会失败,因为match在不同算法具有不同关联类型的情况下,返回的类型略有不同。

我错过了一些明显的东西吗?如何动态创建实现 trait 的东西的实例,然后保留那个东西并通过 trait 接口使用它?

Kor*_*nel 5

The message refers to object safety (longer article). The Digest trait has two incompatibilities:

  1. It uses associated types (this can be worked around by explicitly setting all type parameters to values compatible for all Digest objects).
  2. It has a method (fn result(self) -> …) taking self by value. You won't be able to call it, which ruins usability of this trait.

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();
}
Run Code Online (Sandbox Code Playgroud)