How to pass a `Vec<T>` as `&[&dyn Trait]`?

I'd like to pass a slice of a vector as a slice of trait object, but encountered error. Here's an example:

struct SomeStruct;

trait SomeTrait {}

impl SomeTrait for SomeStruct {}

fn do_sth(a: &[&dyn SomeTrait]) {}

#[test]
fn test_trait() {
    let a = SomeStruct;

    let vector = vec![a];
    let refs = vector.iter().collect::<Vec<_>>();

    do_sth(&[&a]); // This works
    do_sth(refs.as_slice()); // error: expected reference `&[&dyn SomeTrait]`
                             //        found reference `&[&SomeStruct]`
}

I think the type of &[&SomeStruct] is compatible with &[&dyn SomeTrait], but it doesn't work as I expect.

This doesn’t work because a &dyn Trait is a "fat" reference, storing a pointer to the type's vtable in addition to the pointer to the value itself, whereas a &Type is just a normal "thin" reference. But's easy to fix, you just need an explicit coercion:

let refs = vector.iter().map(|a| a as &dyn SomeTrait).collect::<Vec<_>>();
1 Like

That works, thanks very much!

No, it's not a problem with thin vs fat references. The following doesn't compile either, despite &Foo and &Bar both being thin pointers:

struct Foo(Bar);
struct Bar;

impl Deref for Foo {
    type Target = Bar;
    fn deref(&self) -> &Bar {
        &self.0
    }
}

fn take_bar(_: &[&Bar]) {}

fn main() {
    let v: Vec<&Foo> = Vec::new();
    take_bar(&v);
}

The problem is that coercions in a flat buffer can't happen behind indirection like that, since there are two different element types. You simply can't expect to convert a &[T] to &[U] in-place, since there's no guarantee that values of type T are laid out/represented in the same way (or even fit into) as values of type U.

If you want a slice of type [U] out of a slice of type [T], then you'll have to convert each value one-by-one and put the elements into a new vector/array.

2 Likes

But that's what the code does? OP's code collected a Vec<&SomeType> which does not deref-coerce to a &[& dyn SomeTrait]; I added a map call so that the collected Vec has the correct type Vec<&dyn SomeTrait>.

Yes, that's what the code does. (I was only talking about the reason behind that.)

Note: you might want to use generic if you want to avoid creating a new Vec.

fn do_sth<T: SomeTrait + ?Sized>(a: &[&T]) {}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.