Sergey Zalizko
Sergey Zalizko

Reputation: 269

Is it possible to store a value in a struct through a trait?

Editor's note: This code example is from a version of Rust prior to 1.0 and is not syntactically valid Rust 1.0 code. Updated versions of this code produce different errors, but the answers still contain valuable information.

I tried this with and without Box, with and without lifetimes:

trait TraitToImpl {
    fn do_something(self, val: i32);
}

struct Cont {
    value: i32,
}

impl TraitToImpl for Cont {
    fn do_something(self, val: i32) {
        println!("{}", val);
    }
}

struct StoreTrait<'a> {
    field: Box<TraitToImpl + 'a>,
}

fn main() {
    let cont = Box::new(Cont { value: 12 }) as Box<TraitToImpl>;
    let a = StoreTrait { field: cont };
    a.field.do_something(123);
}

All I get is this error:

error: cannot convert to a trait object because trait `TraitToImpl` is not object-safe

Upvotes: 7

Views: 2253

Answers (2)

bluenote10
bluenote10

Reputation: 26530

To understand the source of this error, it may also help to look up the definition of object-safe in the Object Safety RFC:

We say a method m on trait T is object-safe if it is legal (in current Rust) to call x.m(...) where x has type &T, i.e., x is a trait object. If all methods in T are object-safe, then we say T is object-safe.

As pointed out by @DK, x.do_something(...) is illegal since TraitToImpl cannot be passed by value.

Note that object-safety can also be violated by deriving from a non-object-safe trait. E.g.

trait TraitToImpl : Clone {
    fn do_something(&self, val: int); // this method is object-safe
}

would not be object-safe because Clone itself has a non-object-safe method (fn clone(&self) -> Self).

Upvotes: 3

DK.
DK.

Reputation: 58975

The problem is, as the error message says, that the trait TraitToImpl is not object safe. That is, it is not safe to use that particular trait through a reference (i.e. &TraitToImpl or Box<TraitToImpl>.

Specifically, the do_something method takes self by value. Consider: how does the compiler call this method on a Cont that's been placed in a Box<TraitToImpl>? It has to copy the value into an argument of type Cont (which is what the impl expects), but this call has to work for any type of any size that might implement TraitToImpl!

Practical upshot: if you have a trait which contains by-value self, or generics, it cannot be used via a reference. At the point the call happens, the compiler no longer has enough information to actually generate the necessary code.

Maybe try taking &self instead? :)

Upvotes: 8

Related Questions