Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I implement a trait with a generic method?

Tags:

rust

I'm trying to implement a trait which contains a generic method.

trait Trait {
    fn method<T>(&self) -> T;
}

struct Struct;

impl Trait for Struct {
    fn method(&self) -> u8 {
        return 16u8;
    }
}

I get:

error[E0049]: method `method` has 0 type parameters but its trait declaration has 1 type parameter
 --> src/lib.rs:8:5
  |
2 |     fn method<T>(&self) -> T;
  |     ------------------------- expected 1 type parameter
...
8 |     fn method(&self) -> u8 {
  |     ^^^^^^^^^^^^^^^^^^^^^^ found 0 type parameters

How should I write the impl block correctly?

like image 951
baist Avatar asked Oct 31 '18 14:10

baist


2 Answers

Type parameters in functions and methods are universal. This means that for all trait implementers, Trait::method<T> must be implemented for any T with the exact same constraints as those indicated by the trait (in this case, the constraint on T is only the implicit Sized).

The compiler's error message that you indicated suggests that it was still expecting the parameter type T. Instead, your Struct implementation is assuming that T = u8, which is incorrect. The type parameter is decided by the caller of the method rather than the implementer, so T might not always be u8.

If you wish to let the implementer choose a specific type, that has to be materialized in an associated type instead.

trait Trait {
    type Output;

    fn method(&self) -> Self::Output;
}

struct Struct;

impl Trait for Struct {
    type Output = u8;

    fn method(&self) -> u8 {
        16
    }
}

Read also this section of The Rust Programming Language: Specifying placeholder types in trait definitions with associated types.

See also:

  • "Expected type parameter" error in the constructor of a generic struct
  • When is it appropriate to use an associated type versus a generic type?
like image 173
E_net4 stands with Ukraine Avatar answered Sep 21 '22 07:09

E_net4 stands with Ukraine


In addition to the method using an associated type, from this answer, you can also add the generic to the trait.

trait Trait<T> {
    fn method(&self) -> T;
}

impl Trait<u8> for Struct {
    fn method(&self) -> u8 {
        16
    }
}

You use the "associated type" way when there is only one logical form of the trait to use. You can use the generic trait when there is more than one output type that makes sense, for example this is legal:

struct Struct;

trait Trait<T> {
    fn method(&self) -> T;
}

impl Trait<u8> for Struct {
    fn method(&self) -> u8 {
        16
    }
}

impl Trait<String> for Struct {
    fn method(&self) -> String {
        "hello".to_string()
    }
}

fn main() {
    let s = Struct;
    let a: u8 = s.method();
    let b: String = s.method();
    println!("a={}, b={}", a, b);
}

As far as I know, you can not do this with a trait based on associated types.

like image 23
Michael Anderson Avatar answered Sep 22 '22 07:09

Michael Anderson