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

前端 未结 3 1808
醉酒成梦
醉酒成梦 2020-12-11 17:31

Here is an offending example:

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

// Some implementations of these traits
s         


        
相关标签:
3条回答
  • 2020-12-11 17:45

    So the answer to your first question is covered by Tim's answer and is correct, you might 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. Tim's answer answers that by the definition to be higher up in the chain (outside of Example) instead of inside.

    0 讨论(0)
  • 2020-12-11 17:53

    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

    0 讨论(0)
  • 2020-12-11 18:00

    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.

    0 讨论(0)
提交回复
热议问题