首页 文章

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

提问于
浏览
2

我对Rust很新,所以我的术语可能很混乱 .

我想使用hashes crates进行一些散列,我想动态选择在运行时使用哪种算法(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 回答

  • 1

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

    • It uses associated types(这可以通过将所有类型参数显式设置为与所有 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();
    }
    

相关问题