47

I have the following code:

extern crate futures; // 0.1.24

use futures::Future;
use std::io;

struct Context;

pub trait MyTrait {
    fn receive(context: Context) -> Future<Item = (), Error = io::Error>;
}

pub struct MyStruct {
    my_trait: MyTrait,
}

When I try to compile it I get the error message:

error[E0038]: the trait `MyTrait` cannot be made into an object
  --> src/lib.rs:13:5
   |
13 |     my_trait: MyTrait,
   |     ^^^^^^^^^^^^^^^^^ the trait `MyTrait` cannot be made into an object
   |
   = note: method `receive` has no receiver

I think I know why it happens, but how do I refer to the trait from the struct? Is it possible? Maybe there are other ways to implement the same behavior?

Peter Hall
  • 53,120
  • 14
  • 139
  • 204
Alexander
  • 833
  • 2
  • 8
  • 17

3 Answers3

41

You can either add a type parameter to your struct, as in Zernike's answer, or use a trait object.

Using the type parameter is better for performance because each value of T will create a specialized copy of the struct, which allows for static dispatch. A trait object uses dynamic dispatch so it lets you swap the concrete type at runtime.

The trait object approach looks like this:

pub struct MyStruct<'a> {
    my_trait: &'a dyn MyTrait,
}

Or this:

pub struct MyStruct {
    my_trait: Box<dyn MyTrait>,
}

However, in your case, MyStruct cannot be made into an object because receive is a static method. You'd need to change it to take &self or &mut self as its first argument for this to work. There are also other restrictions.

Peter Hall
  • 53,120
  • 14
  • 139
  • 204
17
pub struct MyStruct<T>
where
    T: MyTrait,
{
    my_trait: T,
}

or

pub struct MyStruct<T: MyTrait> {
    my_trait: T,
}

https://doc.rust-lang.org/book/second-edition/ch10-02-traits.html#trait-bounds

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Zernike
  • 1,758
  • 1
  • 16
  • 26
-1

There is a fourth option available, but this will make your struct unsized, a.k.a. you will not be able to create instances of this struct.

pub trait MyTrait {}

pub struct MyStruct {
    my_trait: dyn MyTrait + 'static,
}

This means that MyStruct is an unsized type, and you cannot create direct instances of such type. As Rust currently does not have a way to allocate structs directly on the stack, I do not know if you can create an instance of such a type at all. But hey, it compiles.

Hauleth
  • 22,873
  • 4
  • 61
  • 112
  • 1
    [How do you actually use dynamically sized types in Rust?](https://stackoverflow.com/q/25740916/155423). – Shepmaster Sep 18 '18 at 18:56
  • @Shepmaster I know how use unsized types. But I cannot find a way to instantiate this particular type. I know how to work with types like `struct Foo(str)` but I couldn't find a way to work with this one. – Hauleth Sep 18 '18 at 18:59
  • 1
    I agree that you can't do it at the moment. The link is for when someone stumbles on this answer in X months, whenever it is possible to use DSTs more easily. That Q&A will be updated with new information; it's unlikely that this one will. – Shepmaster Sep 18 '18 at 19:03
  • It does not compile any more: warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! note: for more information, see help: use `dyn` – Code4R7 Aug 25 '22 at 06:12