为什么必须在实现特征的类型的引用集合中指定关联类型?

时间:2018-06-18 08:22:28

标签: rust polymorphism traits

这是一个令人讨厌的例子:

// Some traits
trait Behaviour {
    type Sub: SubBehaviour;
}
trait SubBehaviour {}

// Some implementations of these traits
struct A;
impl Behaviour for A {
    type Sub = B;
}
struct B;
impl SubBehaviour for B {}

// Struct that holds a collection of these traits.
struct Example<'a> {
    behaviours: Vec<&'a Behaviour>,
}

impl<'a> Example<'a> {
    fn add_behaviour<T: Behaviour>(&mut self, b: &'a T) {
        self.behaviours.push(b);
    }
}

fn main() {
    let b = A;
    let mut e = Example {
        behaviours: Vec::new(),
    };
    e.add_behaviour(&b);
}

Playground

我得到了:

error[E0191]: the value of the associated type `Sub` (from the trait `Behaviour`) must be specified
  --> src/main.rs:17:25
   |
17 |     behaviours: Vec<&'a Behaviour>,
   |                         ^^^^^^^^^ missing associated type `Sub` value

为什么必须指定此类型,特别是在这种情况下我们只存储对象的引用?如何才能使此代码生效?

3 个答案:

答案 0 :(得分:2)

您需要指定特征的关联类型(即Behavior<Sub = ???>)。

在所有地方添加相关类型时,它会编译:

struct Example<'a, S: SubBehaviour + 'a> {
    behaviours: Vec<&'a Behaviour<Sub = S>>,
}

impl<'a, S: SubBehaviour> Example<'a, S> {
    fn add_behaviour<T: Behaviour<Sub = S>>(&mut self, b: &'a T) {
        self.behaviours.push(b);
    }
}

在行动on the Playground

中查看此内容

答案 1 :(得分:0)

因此,第一个问题的答案已包含在Tim's answer中,并且是正确的,您可能不希望您的Example是通用的。在这种情况下,您需要使用某种类型的擦除:

// Some traits
trait Behaviour {
    type Sub: SubBehaviour;
}
trait SubBehaviour {}

// Some implementations of these traits
struct A;
impl Behaviour for A {
    type Sub = B;
}

struct B;
impl SubBehaviour for B {}

struct AnyBehaviour {
    closure: Box<Fn()>,
}
impl AnyBehaviour {
    fn new<U: SubBehaviour, T: Behaviour<Sub = U>>(b: &T) -> Self {
        let closure = || {
            //let sub = T::Sub::new();
            println!("Can use T here");
        };

        AnyBehaviour {
            closure: Box::new(closure),
        }
    }
}

// Struct that holds a collection of these traits.
struct Example {
    behaviours: Vec<AnyBehaviour>,
}

impl Example {
    fn add_behaviour<U: SubBehaviour, T: Behaviour<Sub = U>>(&mut self, b: &T) {
        self.behaviours.push(AnyBehaviour::new(b));
    }
}

fn main() {
    let b = A;
    let mut e = Example {
        behaviours: Vec::new(),
    };
    e.add_behaviour(&b);
}

在闭包内,您可以访问所有需要的类型,并使用所需的任何子类型调用traits函数。

为什么会发生这种情况,主要是因为您实际上需要关联类型的定义才能使特征“完整”,以便编译器可以使用它。 Tims的回答是,根据定义,答案是在链中更高(在Example之外),而不是内部。

答案 2 :(得分:0)

所有类型在编译时必须是静态已知的。如果Rust允许Vec的元素使用不同的关联类型,则类型信息可能取决于仅在运行时才知道的索引。

我认为考虑一个较小的示例会有所帮助:

trait Behaviour {
    type T;

    fn make_t(&self) -> T;
}

fn foo(my_vec: Vec<&dyn Behaviour>, index: usize) {
    let t = my_vec[index].make_t(); //Type of t depends on index
}

尽管如此,您仍处在正确的道路上。我假设您介绍了SubBehaviour特性,因为您意识到需要对T进行限制。事实是,在那种情况下,您不再需要关联的类型。

trait SubBehaviour {}

trait Behaviour {
    fn make_t(&self) -> Box<dyn SubBehaviour>;

    fn ref_t(&self) -> &dyn SubBehaviour; // also fine
}

fn some_function(my_vec: Vec<&dyn Behaviour>, index: usize) {
    let t1 = my_vec[index].make_t();
}

唯一的限制是,在Behaviour的定义中,您无法执行任何取决于T大小的操作(例如,将其分配到堆栈上或将其移动),因为T不能由SubBehaviour特性指定。