如何在结构中使用大小特征?

Ran*_*rim 4 rust

我正在尝试使用 AES 板条箱,它提供三种算法:AES128、AES192 和 AES256。我正在尝试创建一个结构,该结构可以通过检测密钥大小来创建正确的算法,并将其保存以供以后使用。

我看到它们都实现了 BlockEncrypt (我只需要加密)特征,但是当我尝试在结构中创建具有这种类型的字段时,即使提供大小,我也会得到“该特征BlockEncrypt无法制成对象。”特征不能被制作成对象,因为它需要Self: Sized“错误。

pub struct MyStruct<'a, T: Sized> {
       ciph: Box< dyn BlockEncrypt<BlockSize = T>>,
}
Run Code Online (Sandbox Code Playgroud)

cdh*_*wie 5

作为超类型的特征Sized不是“对象安全”的,这意味着动态调度使用dyn在这些类型的特征上是不可能的。

您可以相对简洁地完成此操作,方法是对要处理的每种可能类型使用枚举,并结合一个宏,该宏将整齐地生成match动态分派每种枚举可能性所需的所有臂。

为了使这个解释变得简单,我将定义一个新的特征(使用超Sized类型),以便这个答案不依赖于特定的板条箱:

trait SampleTrait: Sized {
    fn method_a(&self, foo: &str);
    fn method_b(&self, foo: i32, bar: String) -> String;
}
Run Code Online (Sandbox Code Playgroud)

现在我们至少需要此特征的两个实现来演示解决方案:

struct ImplA;
struct ImplB;

impl SampleTrait for ImplA {
    fn method_a(&self, foo: &str) {
        println!("<ImplA as SampleTrait>::method_a(): {:?}", foo);
    }
    
    fn method_b(&self, foo: i32, bar: String) -> String {
        println!("<ImplA as SampleTrait>::method_b(): {:?} {:?}", foo, bar);
        format!("from ImplA: {}", bar)
    }
}

impl SampleTrait for ImplB {
    fn method_a(&self, foo: &str) {
        println!("<ImplB as SampleTrait>::method_a(): {:?}", foo);
    }
    
    fn method_b(&self, foo: i32, bar: String) -> String {
        println!("<ImplB as SampleTrait>::method_b(): {:?} {:?}", foo, bar);
        format!("from ImplB: {}", bar)
    }
}
Run Code Online (Sandbox Code Playgroud)

枚举只需要最少化您需要处理的每种可能的类型:

enum DynSampleTrait {
    ImplA(ImplA),
    ImplB(ImplB),
}
Run Code Online (Sandbox Code Playgroud)

现在要对此进行调度,您必须执行以下操作:

match value {
    DynSampleTrait::ImplA(v) => v.method_a("something"),
    DynSampleTrait::ImplB(v) => v.method_a("something"),
}
Run Code Online (Sandbox Code Playgroud)

为了避免重复,让我们声明一个宏来match为我们生成整个内容:

macro_rules! dyn_sample_trait_call {
    (($m:expr) $v:ident => $code:block) => {
        match $m {
            DynSampleTrait::ImplA($v) => $code,
            DynSampleTrait::ImplB($v) => $code,
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们可以像这样使用宏:

dyn_sample_trait_call!((value) v => { v.method_a("something") })
Run Code Online (Sandbox Code Playgroud)

value进入匹配表达式,并且v是解包枚举可能性所持有的内部值的标识符。它们是分开的,以便您可以匹配引用,如果您不想使用拥有的DynSampleTrait

dyn_sample_trait_call!((&value) v => { v.method_a("something") })
Run Code Online (Sandbox Code Playgroud)

游乐场