How can I return an impl trait from a function dynamically when this trait contains a method which returns itself an impl trait?

85 views Asked by At

As the following code shows:

  trait T2impl {}

  struct S4T2impl;

  impl T2impl for S4T2impl{}

  trait TimplMethod {
   fn f() -> impl T2impl;
  }

  struct S4TimplMethod;

  impl TimplMethod for S4TimplMethod {
   fn f() -> impl T2impl {
    S4T2impl
   }
  }

  fn f1() -> impl TimplMethod {
   S4TimplMethod
  }
 
  // so far so good
  // but I want to return one of more TimplMethod implementations, so I need a dynamic approach:

  // fn f2() -> Box<dyn TimplMethod> {
  //  Box::new( S4TimplMethod)
  // }

Here I get the error message:

error[E0038]: the trait `TimplMethod` cannot be made into an object
   --> src/main.rs:158:18
    |
158 |   fn f2() -> Box<dyn TimplMethod> {
    |                  ^^^^^^^^^^^^^^^ `TimplMethod` cannot be made into an object
    |
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
   --> src/main.rs:140:7

How can I solve this problem?

[update]

As I can see in some answers some suggest to change the TimplMethod trait. This is not what I am looking for. Please consider all code from line 1 to function f1 as read-only.

1

There are 1 answers

5
user4815162342 On

If you can't change TimplMethod, you can create an object-safe helper trait that is defined for all types that define TimplMethod, and return that:

// TimplMethod and its implementers unchanged

// helper trait that provides a boxed T2impl in its f()
trait TimplMethodErased {
    fn f(&self) -> Box<dyn T2impl + '_>;
}

// blanket implementation for the helper trait for every type
// that implements the original
impl<T> TimplMethodErased for T
where
    T: TimplMethod,
{
    fn f(&self) -> Box<dyn T2impl + '_> {
        Box::new(T::f())
    }
}

fn f1() -> Box<dyn TimplMethodErased> {
    Box::new(S4TimplMethod)
}

Playground