Vector covariant in its element type

Consider the following snippet:

struct Person;

trait Nameable {}

impl Nameable for Person {}

fn print(nameables: &Vec<&dyn Nameable>) {}

fn main() {
    let p1 = Person;
    let people = vec![&p1];

    print(&people);

This doesn't compile with an error "expected reference &Vec<&dyn Nameable> found reference `&Vec<&Person>". Fixing this error requires the following:

    print(&people.iter().map(|person| *person as &dyn Nameable).collect());

which feels unnecessary, requires extra code, and wastes a heap allocation. I believe the reason for this error is that Vec isn't covariant in its element type. Since the destination type (&Vec<&dyn Nameable>) is immutable, I believe, there wouldn't be a problem if Rust allowed the first snippet to compile.

Is there a deeper reason? Is there another way to avoid the allocation?

let people: Vec<&dyn Nameable> = vec![&p1]; works.

In general you can't be covariant over trait objects like that because a reference to a static type and a reference to a trait object are different sizes. The trait object reference is twice the size since it needs a pointer to the vtable for the trait in addition to pointer to the value.

Got it (I hadn't considered that a trait object reference is a fat pointer).

In my case, I do need people: Vec<&Person> for other purposes, so I guess I can't avoid that extra allocation.

If you need to pass a Vec, then yes. If you only need to pass something that represents a sequence of &dyn Nameable you can accept an Iterator instead and just map each Person into a trait object.

1 Like

Thanks for your insight.

It is, but that doesn't mean what you think it means. The only subtypes that exist in Rust (and thus the only place variance matters) is with respect to lifetimes (including higher-ranked ones). In particular, there is no subtyping relationship between implementors of a trait and trait objects. Instead you're running into where unsized coercions can or cannot happen.

The practical considerations seem to have already been answered, so I'll leave it there.

4 Likes