Using a `Vec<&dyn A>`

Hi rustaceans,

I'd like to use a Vec<&dyn> to avoid Box, but rust tries to tell me something I don't understand. Can somebody interpret what it tells me?

Or does Storing elements in a Vec where each element implements multiple dyn Traits - #11 by alice mean that whatever I'm trying to do, I should better use Vec<Box<dyn A>>?

trait A {
    fn print(&self);
}

struct Aimpl {
    s: String
}

impl A for Aimpl {
    fn print(&self){
        println!("{}", self.s);
    }
}

fn prints(v: Vec<&dyn A>){
    for a in v {
        a.print();
    }
}

fn main(){
    // This doesn't work because &Aimpl 
    // "creates a temporary which is freed while still in use"
    //let v: Vec<&dyn A> = vec![&Aimpl{s: "123".into()}, &Aimpl{s: "456".into()}];
    // This doesn't work, because
    // "prints(v) expected trait object 'dyn A', found struct 'Aimpl"
    let v = vec![&Aimpl{s: "123".into()}, &Aimpl{s: "456".into()}];
    prints(v);
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
  --> src/main.rs:26:12
   |
26 |     prints(v);
   |     ------ ^ expected trait object `dyn A`, found struct `Aimpl`
   |     |
   |     arguments to this function are incorrect
   |
   = note: expected struct `Vec<&dyn A>`
              found struct `Vec<&Aimpl>`
note: function defined here
  --> src/main.rs:15:4
   |
15 | fn prints(v: Vec<&dyn A>){
   |    ^^^^^^ --------------

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` due to previous error

Yes. You cannot store a reference to something that isn't already stored somewhere else (hence the t"emporary which is freed while still in use" error).

1 Like

Thanks a lot - but now I'm stuck again:

fn prints(v: Vec<Box<dyn A>>){
...
}

fn main(){
    let v = vec![Box::new(Aimpl{s: "123".into()})];
    prints(v);
}

Still gives an error I don't understand: Rust Playground

30 |     prints(v);
   |     ------ ^ expected trait object `dyn A`, found struct `Aimpl`
   |     |
   |     arguments to this function are incorrect
   |
   = note: expected struct `Vec<Box<(dyn A + 'static)>>`
              found struct `Vec<Box<Aimpl>>`

If you specify the type of v to ensure you get trait objects it works.

let v: Vec<Box<dyn A>> = // unchanged ... 
2 Likes

To elaborate, you can't perform unsized coercion (from Box<Aimpl> to Box<dyn A>) through the outer Vec, so you have to have a Vec<Box<dyn A>> from the start. (There's nothing in the unannotated initialization to nudge the compiler to infer the coercion, so you got a Vec<Box<Aimpl>>.)

1 Like

Combining it all into one line actually works too

prints(vec![Box::new(Aimpl{s: "123".into()})]);

I think the difference there is that having an intermediary binding gave the type system somewhere to attach the type of the Vec to, and after that point the type was considered known. So inference didn't get involved when you passed v to prints because it had already decided it was done with v.

1 Like

This is one of those moments I wish that rust would do some more magic...

It depends; your simple example in itself can be made work trivially by storing the Aimpls in variables and taking the address of those variables:

fn main(){
    let a1 = Aimpl{s: "123".into()};
    let a2 = Aimpl{s: "456".into()};
    let v: Vec<&dyn A> = vec![&a1, &a2];
    prints(v);
}