我怎样才能在Rust中创建一个trait的变量并关联一个类型?

disho6za  于 2023-03-02  发布在  其他
关注(0)|答案(1)|浏览(105)

我想创建一个包含trait的变量。trait的实现在编译时是未知的。因此,我需要一个trait对象。这适用于"普通" trait,但当trait有一个在编译时未知的关联类型时就不行了。
假设AssTrait是一个trait关联一个类型,AssTraitImpl是一个struct实现这个trait(见下面的例子),那么AssTraitImpl示例的trait对象就可以指向vtable,vtable代表AssTraitImpl实现的方法,或者我错了?

示例

下面的代码不起作用。但是,如果我们从trait中移除关联的类型,它就可以起作用。

trait AssTrait {
    type Item;
}

struct AssTraitImpl {
}

impl AssTrait for AssTraitImpl {
    type Item = i32;
}

fn main() {
    let var: &dyn AssTrait;
}

我收到此错误消息:

error[E0191]: the value of the associated type `Item` (from trait `AssTrait`) must be specified
  --> src/main.rs:20:20
   |
9  |     type Item;
   |     --------- `Item` defined here
...
20 |     let var : &dyn AssTrait;
   |                    ^^^^^^^^ help: specify the associated type: `AssTrait<Item = Type>`
3pvhb19x

3pvhb19x1#

因为类型在运行时之前是未知的,所以关联的类型也是未知的。* 从技术上讲 *,在您的示例中,Rust不需要关心这一点,因为您实际上并没有使用它。但这是一个非常不常见的示例代码。一个关联的类型不用于trait的任何方法参数或返回类型是不常见的。Rust选择不允许未指定的关联类型,而不是检查每个方法是否未使用。也许将来可以改变这一点,因为它将是向后兼容的,但除了一些非常罕见的用例外,我看不到它有多大价值。
正如我所暗示的,如果指定了关联类型,那么它是允许的:

let var: &dyn AssTrait<Item = i32>;

这是因为Rust编译器知道Item的类型,但是它强制您使用的所有实现也具有Item = i32
你也可以通过为关联类型使用trait对象来使它更灵活一些:

trait ItemBehavior {}

let var: &dyn AssTrait<Item = dyn ItemBehavior>;

// Or, depending on usage, perhaps:
let var: &dyn AssTrait<Item = &dyn ItemBehavior>;
// Or maybe:
let var: &dyn AssTrait<Item = Box<dyn ItemBehavior>>;

现在可以为每个可能关联的类型实现ItemBehaviour,并在使用该类型时为其提供所需的任何方法或约束。
另一种可能的解决方案是创建另一个没有关联类型的trait,并为实现其他trait的所有类型实现它:

trait NonAssTrait {}

impl<T: AssTrait> NonAssTrait for T {} 

fn main() {
    let var: &dyn NonAssTrait;
}

NonAssTrait的实现可以将任何方法调用委托给AssTrait示例,前提是它们不公开关联的类型。

相关问题