manonthemat
manonthemat

Reputation: 6251

Expected generic type, but found concrete type (that implements trait bound)

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

Answers (1)

Simon Whitehead
Simon Whitehead

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

Related Questions