如何包装具有相关类型的特征?

时间:2017-12-29 20:00:26

标签: rust traits associated-types

我对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将返回稍微不同的类型。

我错过了一些明显的东西吗?如何动态创建实现特征的事物的实例,然后保持该事物并通过特征界面使用它?

1 个答案:

答案 0 :(得分:2)

该消息引用object safetylonger article)。 Digest trait有两个不兼容性:

  1. uses associated types(可以通过将所有类型参数显式设置为与所有Digest个对象兼容的值来解决此问题。)
  2. 它有一个方法(fn result(self) -> …)按值self。你无法打电话给它,这会破坏这种特性的可用性。
  3. 创建特征对象后,将删除有关其特定于子类型的功能(如内存布局或关联类型)的信息。对特征对象方法的所有调用都是通过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();
    }