Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to move a value out of an object-safe trait object?

A Mech carries a driver, which is a Named entity. At run-time, an omitted Mech constructor consults external source for the specific type of driver to use.

trait Named {
    fn name(self) -> String;
}

struct Person {
    first_name: String,
    last_name: String
}

impl Named for Person {
    fn name(self) -> String {
        format!("{} {}", self.first_name, self.last_name)
    }
}

pub struct Mech<'a> {
    driver: Box<Named + 'a>,
}

impl<'a> Mech<'a> {
    pub fn driver_name(self) -> String {
        self.driver.name()
    }
}

Method driver_name returns ownership to a String, for it to be further used in chained calls (in actual code it's a Command). It fails compilation with:

error[E0161]: cannot move a value of type Named + 'a: the size of Named + 'a cannot be statically determined
  --> src/lib.rs:22:9
   |
22 |         self.driver.name()
   |         ^^^^^^^^^^^

Making the trait Sized fails the object safety:

trait Named: Sized {
    fn name(self) -> String;
}

error[E0038]: the trait `Named` cannot be made into an object
  --> src/lib.rs:17:5
   |
17 |     driver: Box<Named + 'a>,
   |     ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Named` cannot be made into an object
   |
   = note: the trait cannot require that `Self : Sized`

Is there a way to make this pattern happen?

Is there anything fundamental that I seem to be missing?

In case this is impossible to achieve, what's a good way to work around it?

like image 221
mwgkgk Avatar asked Jun 20 '18 13:06

mwgkgk


Video Answer


1 Answers

As the compiler hinted, the trait cannot be statically determined because you are dealing with dynamic dispatch. Ownership is still possible in this scenario using self: Box<Self>.

trait Named {
    fn name(self: Box<Self>) -> String;
}

struct Person {
    first_name: String,
    last_name: String,
}

impl Named for Person {
    fn name(self: Box<Self>) -> String {
        format!("{} {}", self.first_name, self.last_name)
    }
}

pub struct Mech<'a> {
    driver: Box<Named + 'a>,
}

impl<'a> Mech<'a> {
    pub fn driver_name(self) -> String {
        self.driver.name()
    }
}

fn main() {}
like image 140
Caio Avatar answered Oct 27 '22 00:10

Caio