12

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?

Lukas Kalbertodt
  • 79,749
  • 26
  • 255
  • 305
mwgkgk
  • 173
  • 8
  • 8
    Why `self` instead of `&self`? – Shepmaster Jun 20 '18 at 13:33
  • Can't think of another way other than to adopt the take pattern: `fn take_name(&mut self) -> String` is object safe, but requires an extension to the type's state domain. – E_net4 Jun 20 '18 at 13:50

1 Answers1

16

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() {}
Caio
  • 3,178
  • 6
  • 37
  • 52
  • 1
    thanks so much, i was looking a loong time for something like that. I would like to ask another thing, if you can help me on that account: While Rust Documentation is generally awsome, I did scour a lot of it for a solution like that, and I'm sure theres nothing like that in Rust by Example, the Rust Book or the Nomicum. And I'm still not completely comfortable with most of the stuff in the official reference, where I'd have found that I guess. Is there another source where I could see more advanced patterns like that in practice? – Sam96 Dec 29 '20 at 21:37
  • 1
    @Sam96 (1) The Rust subreddit has some eventual blog posts about good patterns and technical stuff; (2) The official Zulip and Discord channels are great places to ask complicated questions to smart people and (3) Personally, most of my Rust knowledge were acquired by hacking open-source projects – Caio Dec 30 '20 at 11:17
  • Good job! Useful here as well: https://github.com/fzyzcjy/flutter_rust_bridge/pull/582#discussion_r934064949 – ch271828n Aug 01 '22 at 00:39