simao
simao

Reputation: 15529

Passing different trait implementations depending on runtime value

I am trying to use something equivalent to the following code:

struct MyStruct {}

struct MyStruct2 {}

trait CanDoIt {
    fn do_it(&self) -> String;
}

impl CanDoIt for MyStruct {
    fn do_it(&self) -> String {
        "done".into()
    }
}


impl CanDoIt for MyStruct2 {
    fn do_it(&self) -> String {
        "done 2".into()
    }
}


fn do_a<T: CanDoIt>(value: T) -> String {
    value.do_it()
}


fn main() {
    let s: dyn CanDoIt = if true {
        MyStruct {}
    } else {
        MyStruct2 {}
    };

    println!("Done it: {}", do_a(s))
}

The if condition could be dependent on some runtime value, for example whether the hardware supports some feature.

I tried different things, like making the struct also implement Sized, but I always get some compile error like this:

   |
29 |     let s: dyn CanDoIt = if true {
   |         ^ doesn't have a size known at compile-time
   |

What is the solution for when you need to pass around different implementations depending on some runtime value?

Upvotes: 0

Views: 61

Answers (1)

rnstlr
rnstlr

Reputation: 1803

Traits like your CanDoIt are non sized, because they are not a concrete type and thus can't be stored as values directly. One can only store a trait object like Box<dyn CanDoIt> or &dyn CanDoIt

So for your example using &dyn CanDoIt it would look like this:

struct MyStruct {}

struct MyStruct2 {}

trait CanDoIt {
    fn do_it(&self) -> String;
}

impl CanDoIt for MyStruct {
    fn do_it(&self) -> String {
        "done".into()
    }
}


impl CanDoIt for MyStruct2 {
    fn do_it(&self) -> String {
        "done 2".into()
    }
}


fn do_a(value: &dyn CanDoIt) -> String {
    value.do_it()
}


fn main() {
    let s: &dyn CanDoIt = if true {
        &MyStruct {}
    } else {
        &MyStruct2 {}
    };

    println!("Done it: {}", do_a(s))
}

Upvotes: 1

Related Questions