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 ?
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.
Object-safe traits are traits with methods that follow these two rules: the return type is not Self. there are no generic types parameters.
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.
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.
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.
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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With