A problem with trait implementations and trait objects


#1

I’ve hit a weird snag. The following code fails to compile:

trait Foo: Clone {
    fn bar(&self) -> usize;
}

struct Baz {
    bits: Vec<Box<Foo>>,
}

impl Baz {
    fn bunk(&self) -> usize {
        let mut ans = 0;
        for b in &self.bits {
            ans = ans + b.bar();
        }
        ans
    }
}

fn main() {    
}

with an error about Foo not implementing either Clone or Foo, but if I remove the requirement that Foo extends Clone, then it compiles just fine.

Ultimately, I’d like a Vec of objects each of which implements Foo, but which are not necessarily all the same type. I’d then like to call the bar method on each of these objects (which I know must be implemented because those objects implement Foo, even though I understand that Foo itself does not implement Foo). Is there some way I can actually do this without using tuples (since I don’t know at compile-time how many objects which implement Foo I’ll have)?


#2

This is because Clone (and as a consequence, anything that requires it) is not object safe. Specifically:

    fn clone(&self) -> Self;

In order for the compiler to construct a Box<Foo>, it has to somehow make every method on the value compatible at the binary level. Given the following:

#[derive(Clone)] struct A(u8);
#[derive(Clone)] struct B([u8; 128]);

How big is the return value from Clone::clone? This is a trick question: there is no answer. A::clone returns 1 byte, B::clone returns 128 bytes. There is no way for the compiler to reconcile these two.

As such, if a trait includes any methods that involve self or Self (or are static, but for different reasons), that trait and all traits that require it are not object safe.

See also the Trait Objects chapter of the Rust Book.