Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trait Object is not Object-safe error

Tags:

rust

The following code does not compile for me.

trait A {
    fn fun0(&self);
    fn fun2(&self) -> Option<Box<Self>>;
}

struct B0 {
    id: usize,
}

impl A for B0 {
    fn fun0(&self) { println!("Value: {:?}", self.id); }
    fn fun2(&self) -> Option<Box<Self>> { Option::None }
}

struct B1 {
    id: isize,
}

impl A for B1 {
    fn fun0(&self) { println!("Value: {:?}", self.id); }
    fn fun2(&self) -> Option<Box<Self>> { Option::Some(Box::new(B1 { id: self.id, })) }
}

enum C {
    None,
    Put { object: Box<A>, },
}

fn fun1(values: Vec<C>) {
    for it in values.iter() {
        match *it {
            C::Put { object: ref val, } => val.fun0(),
            C::None => (),
        };
    }
}

fn main() {
    let obj_b0 = Box::new(B0 { id: 778, });
    let obj_b1 = Box::new(B1 { id: -8778, });
    let obj_c0 = C::Put { object: obj_b0, };
    let obj_c1 = C::Put { object: obj_b1, };
    let mut vec = Vec::new();
    vec.push(obj_c0);
    vec.push(obj_c1);
    fun1(vec);
}

gives an error:

cargo run
   Compiling misc v0.0.1 (file:///home/spandan/virtualization/coding/my/rust-tests/misc/misc)
src/main.rs:188:48: 188:54 error: the trait `A` is not implemented for the type `A` [E0277]
src/main.rs:188             C::Put { object: ref val, } => val.fun0(),
                                                               ^~~~~~
src/main.rs:197:35: 197:41 error: cannot convert to a trait object because trait `A` is not object-safe [E0038]
src/main.rs:197     let obj_c0 = C::Put { object: obj_b0, };
                                                  ^~~~~~
src/main.rs:197:35: 197:41 note: method `fun2` references the `Self` type in its arguments or return type
src/main.rs:197     let obj_c0 = C::Put { object: obj_b0, };
                                                  ^~~~~~
src/main.rs:198:35: 198:41 error: cannot convert to a trait object because trait `A` is not object-safe [E0038]
src/main.rs:198     let obj_c1 = C::Put { object: obj_b1, };
                                                  ^~~~~~
src/main.rs:198:35: 198:41 note: method `fun2` references the `Self` type in its arguments or return type
src/main.rs:198     let obj_c1 = C::Put { object: obj_b1, };
                                                  ^~~~~~
error: aborting due to 3 previous errors
Could not compile `misc`.

working with

rustc --version
rustc 1.0.0-nightly (00978a987 2015-04-18) (built 2015-04-19)

The problem appears when fun2(&self) is brought into the picture. It compiles and runs fine if fun0 is the only existing function in the trait. But my code needs such a pattern - How do i do it ?

Edit: the correct answer for the above has been given here (https://stackoverflow.com/a/29985438/1060004) . But i am running into the same problem if i remove the &self from function signature (ie., make it static):

fn fun2() -> Option<Box<A>>

what is the issue now ?

like image 971
ustulation Avatar asked May 01 '15 10:05

ustulation


People also ask

What is a trait object?

A trait object is an opaque value of another type that implements a set of traits. The set of traits is made up of an object safe base trait plus any number of auto traits. Trait objects implement the base trait, its auto traits, and any supertraits of the base trait.

What is object safe?

Object-safe traits are traits with methods that follow these two rules: the return type is not Self. there are no generic types parameters.

What is dynamic dispatch in Rust?

Dynamic dispatch. Rust provides dynamic dispatch through a feature called 'trait objects'. Trait objects, like &Foo or Box<Foo> , are normal values that store a value of any type that implements the given trait, where the precise type can only be known at runtime.

What is sized in Rust?

In Rust a type is sized if its size in bytes can be determined at compile-time. Determining a type's size is important for being able to allocate enough space for instances of that type on the stack. Sized types can be passed around by value or by reference.


2 Answers

As you have noticed, the problem vanishes when you remove fun2 method. Let's look at it more closely:

fn fun2(&self) -> Option<Box<Self>>;

Note that its output type contains Self, that is, the type which the trait is implemented for. For example, if A is implemented for String, it would be String:

impl A for String {
    fn fun2(&self) -> Option<Box<String>> { ... }
}

However! With the trait objects the actual type of the value is erased, and the only thing that we know about trait objects is that it is a value which implements the trait, but we don't know the actual type the trait is implemented for. Trait object methods are dispatched dynamically, so the program selects the actual method to call at runtime. These methods have to behave identically, that is, accept the same number of parameters of the same size (pairwise) and return values of the same size too. If a method uses Self somewhere in its signature, like fun2, its implementations won't be compatible with each other because they would need to operate on values of different size, and hence such methods can't be unified.

Such methods (which can't work with trait objects) are called object-unsafe (or not object-safe). If a trait contains such methods, it can't be made a trait object - it is also called not object-safe.

What would work, I believe, is that you can make the trait return a trait object:

fn fun2(&self) -> Option<Box<A>>

Now the dependency on the actual type is lifted, and the trait becomes object-safe again.

like image 127
Vladimir Matveev Avatar answered Sep 28 '22 10:09

Vladimir Matveev


Well, the error message pretty much spells out the immediate problem: you're trying to use a non-object-safe trait in an object context, and you can't do that.

You can remove fun2 from the trait A, and define it in a different trait: its presence will prevent A from ever being used as a "trait object"; so &A, Box<A>, etc. will all out of the question. Each type can then implement both of these traits.

Another alternative is to change fun2 so that its result does not contain the Self type; your example is too abstract to know, but would Option<Box<A>> be acceptable?

As for why: in order for a trait to be used as a trait object, the compiler has to be able to generate a vtable for the trait's methods so that it can do dynamic, runtime dispatch. This means that every method in the trait has to be implementable with the same types (the self parameter is a special case). So what does fun2 return? It has to return an Option<Box<Self>>, but Self is a different type for every implementation! There's no possible way to unify it!

like image 38
DK. Avatar answered Sep 28 '22 09:09

DK.