1

I would like to write a function which returns structs which are implementing a common trait.

If my function specifies the return type -> impl MyTrait, I fail to be compliant when using a match because the match must return the same type. Example:

fn get_a_struct(an_enum: MyEnum) -> impl MyTrait {
    match an_enum {
        MyEnum::MyEnumFoo => MyStruct1 {},
        MyEnum::MyEnumBar => MyStruct2 {},
    }
}

Which produces:

error[E0308]: match arms have incompatible types
  --> src/main.rs:22:5
   |
22 | /     match an_enum {
23 | |         MyEnum::MyEnumFoo => MyStruct1{},
24 | |         MyEnum::MyEnumBar => MyStruct2{},
   | |                              ------------- match arm with an incompatible type
25 | |     }
   | |_____^ expected struct `MyStruct1`, found struct `MyStruct2`
   |
   = note: expected type `MyStruct1`
              found type `MyStruct2`

If I try it with a Box, like this:

trait MyTrait {
    fn my_func() {}
}

enum MyEnum {
    MyEnumFoo,
    MyEnumBar,
}

struct MyStruct1 {}
struct MyStruct2 {}

impl MyTrait for MyStruct1 {
    fn my_func() {
        println!("Hello world from MyStruct1")
    }
}

impl MyTrait for MyStruct2 {
    fn my_func() {
        println!("Hello world from MyStruct2")
    }
}

fn get_a_struct(an_enum: MyEnum) -> Box<MyTrait> {
    match an_enum {
        MyEnum::MyEnumFoo => Box::new(MyStruct1 {}),
        MyEnum::MyEnumBar => Box::new(MyStruct2 {}),
    }
}
error[E0038]: the trait `MyTrait` cannot be made into an object
  --> src/main.rs:21:1
   |
21 | fn get_a_struct(an_enum: MyEnum) -> Box<MyTrait> {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `MyTrait` cannot be made into an object
   |
   = note: method `my_func` has no receiver

I don't know how to use a trait in this case.

How can I write a function which returns structs which are implementing the same trait?

A partial response can be found in Why can impl trait not be used to return multiple / conditional types?, but none of the answers address the object-safety issue.

Similar behaviour in OOP can specify a return type by an interface.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
bux
  • 7,087
  • 11
  • 45
  • 86

1 Answers1

1

As the compiler message says, you need to add a receiver to the my_func method: fn my_func() -> fn my_func(&self)

The reason this is necessary is because it needs to be object-safe. The requirements are detailed in RFC-0255

The specific requirement to your case is

must have a receiver that has type Self or which dereferences to the Self type;

for now, this means self, &self, &mut self, or self: Box<Self>, but eventually this should be extended to custom types like self: Rc<Self> and so forth.

use std::fmt::Debug;

fn main() {
    println!("Foo => {:?}", get_a_struct(MyEnum::MyEnumFoo));
    println!("Bar => {:?}", get_a_struct(MyEnum::MyEnumBar));
}

trait MyTrait :Debug{
    fn my_func(&self) {}
}

enum MyEnum {
    MyEnumFoo,
    MyEnumBar,
}

#[derive(Debug)]
struct MyStruct1 {}

#[derive(Debug)]
struct MyStruct2 {}

impl MyTrait for MyStruct1 {
    fn my_func(&self) {
        println!("Hello world from MyStruct1")
    }
}

impl MyTrait for MyStruct2 {
    fn my_func(&self) {
        println!("Hello world from MyStruct2")
    }
}

fn get_a_struct(an_enum: MyEnum) -> Box<dyn MyTrait> {
    match an_enum {
        MyEnum::MyEnumFoo => Box::new(MyStruct1 {}),
        MyEnum::MyEnumBar => Box::new(MyStruct2 {}),
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Svetlin Zarev
  • 14,713
  • 4
  • 53
  • 82
  • 1
    How does your answer differ from the suggested duplicate? https://stackoverflow.com/questions/45116984/the-trait-cannot-be-made-into-an-object – hellow Apr 10 '19 at 07:30
  • I didn't see the link when I started writing it; I'm also going to provide more details from rfc0255 – Svetlin Zarev Apr 10 '19 at 07:32
  • 1
    The first duplicate link addresses only the first part of the question and the second duplicate link discusses another problem. So this question is not a duplicate. Also none of the answers address the object-safety issue. – Svetlin Zarev Apr 10 '19 at 07:52
  • 2
    Yes, it's a combination of both. The first dup addresses the general problem of `impl Trait` as return type, and the second dup addresses the *"the trait `MyTrait` cannot be made into an object"* thing. – hellow Apr 10 '19 at 08:07