Reputation: 6251
I have two methods that do very similar things that I do want to refactor using generics and trait bounds.
Here are the two working methods of my example.
fn some_method_a(some_arg: u8) -> (Mode, Vec<A>)
{
let mode = match some_arg {
0 => Mode::ZERO,
_ => Mode::NOTZERO,
};
let some_as = vec![A::new(0), A::new(1)];
(mode, some_as)
}
fn some_method_b(some_arg: u8) -> (Mode, Vec<B>)
{
let mode = match some_arg {
0 => Mode::ZERO,
_ => Mode::NOTZERO,
};
let some_bs = vec![B::new("Testing".to_string())];
(mode, some_bs)
}
This is the method that should replace the two.
fn some_method<T>(some_arg: u8) -> (Mode, Vec<T>)
where
T: Filterable,
{
let mode = match some_arg {
0 => Mode::ZERO,
_ => Mode::NOTZERO,
};
match &mode {
&Mode::ZERO => (mode, vec![A::new(0), A::new(1)]),
_ => (mode, vec![B::new("Testing".to_string())]),
}
}
However, I'm getting this error now, even though A (and B) implement the Filterable trait, which I've specified as a trait bound for the method.
error[E0308]: mismatched types
--> src/main.rs:11:36
|
11 | &Mode::ZERO => (mode, vec![A::new(0), A::new(1)]),
| ^^^^^^^^^ expected type parameter, found struct `A`
|
= note: expected type `T`
found type `A`
Permalink to playground Here's the rest of the program:
#[derive(Debug)]
enum Mode {
ZERO,
NOTZERO,
}
trait Filterable {
fn get_some_data(&self) -> u8 {
0
}
}
struct A {
some_data: u8,
}
struct B {
some_other_data: String,
}
impl A {
fn new(some_data: u8) -> A {
A { some_data }
}
}
impl B {
fn new(some_other_data: String) -> B {
B { some_other_data }
}
}
impl Filterable for A {
fn get_some_data(&self) -> u8 {
self.some_data
}
}
impl Filterable for B {}
What needs to be done for some_method to return a tuple of mode and a vector of either A or B structs?
Upvotes: 4
Views: 694
Reputation: 65079
My understanding of Rust generics is that they are a little more like C++ templates than what you might be used to, in the sense that they are monomorphised and expanded at compile time.
Essentially what this does:
fn some_method<T>(some_arg: u8) -> (Mode, Vec<T>)
where T: Filterable,
...is (if it compiled successfully) create two versions of this method. One that expects to return (Mode, Vec<A>)
and one that expects to return (Mode, Vec<B>)
. This doesn't really make sense at all when you think about it that way.
What you want, is to return a vector of things that implement the trait Filterable
. So since traits are unsized, you need to wrap them in something that has a known size. Box
'ing them does the trick here:
fn some_method(some_arg: u8) -> (Mode, Vec<Box<Filterable>>)
This removes the generics entirely in favour of just returning a vector of boxed Filterable
-implementing instances.
Of course, the method body now also must become this:
match &mode {
&Mode::ZERO => (mode, vec![Box::new(A::new(0)), Box::new(A::new(1))]),
_ => (mode, vec![Box::new(B::new("Testing".to_string()))]),
}
Resulting in the code compiling.
Here it is running on the Playground
Upvotes: 2