Why must the associated type be specified in a collection of references to types implementing a trait?

好久不见. 提交于 2019-11-29 14:22:40
Tim Diekmann

You need to specify the associated type of the trait (i.e. Behavior<Sub = ???>).

When adding the associated type at all places, it compiles:

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);
    }
}

See this in action on the Playground

So the answer to your first question is covered by Tim's answer and is correct, you might want not want your Example to be generic. In that case, you need to use some sort of type erasure:

// 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);
}

Within the closure, you have access to all the types needed call the traits functions with whatever subtype needed.

Why this happens, is mostly because you actually need a definition of the associated type in order for the trait to be "complete" so the compiler can work with it. Tims answer answers that by the definition to be higher up in the chain (outside of Example) instead of inside.

Markus Klein

All types must be statically known at compile time. If Rust would allow different associated types for elements of a Vec, type information could depend on indices which are only known at runtime.

I find it helpful to consider a smaller example:

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
}

You were on the right track to fixing this though. I assume you introduced the SubBehaviour trait because you realized you need to put restrictions of what T can be. The thing is, in that case you don't need an associated type anymore.

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();
}

The only limitation is that in your definition of Behaviour you can not do anything which would depend on the size of T, (like allocating it on the stack or moving it) since the size of T can not be specified by the SubBehaviour trait.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!