Let's look at a slightly more general example. We will define a trait with a function that takes and returns a generic type:
trait Foo {
fn foo<T>(&self, value: T) -> T;
}
struct Bar;
impl Foo for Bar {
fn foo<u8>(&self, value: u8) -> u8 {
value
}
// Equivalent to
// fn foo<T>(&self, value: T) -> T {
// value
// }
}
As oli_obk - ker has already explained, fn foo<u8>(&self, value: u8) -> u8
defines a generic type parameter called u8
which shadows the built in type u8
. This is allowed for forwards compatibility reasons — what if you decided to call your generic type Fuzzy
and then a crate (or the standard library!) introduced a type also called Fuzzy
? If shadowing wasn't allowed, your code would stop compiling!
However, you should avoid using generic type parameters that are existing types - as shown, it's just confusing.
Many times, people fall into this trap because they are trying to specify a concrete type for a generic parameter. This shows that there is a misunderstanding of how generic types work: generic types are chosen by the caller of the function; the implementation doesn't get to pick what they are!
The solutions outlined in the other answer remove the ability for the caller to choose the generic.
Moving the generic type to the trait and only implementing it for a handful of types means that there's only a small set of implementations available. If the caller tries to use a type that doesn't have a corresponding implementation, it will just fail to compile.
Choosing an associated type is designed to allow the implementor of the trait to choose the type, and is often the correct solution in this case.
See When is it appropriate to use an associated type versus a generic type? for more details on picking between the two.
This problem is more common for people learning Rust that haven't yet internalized what the various syntaxes of generics do. A quick refresher...
Functions / methods:
fn foo<T>(a: T) -> T
// ^-^ declares a generic type parameter
fn foo<T>(a: T) -> T
// ^ ^ a type, which can use a previously declared parameter
Traits:
trait Foo<T>
// ^-^ declares a generic type parameter
Structs / enums:
enum Wuuf<T>
// ^-^ declares a generic type parameter
struct Quux<T>
// ^-^ declares a generic type parameter
Implementations:
impl<T> Foo<T> for Bar<T>
// ^-^ declares a generic type parameter
impl<T> Foo<T> for Bar<T>
// ^----^ ^----^ a type, which can use a previously declared parameter
In these examples, only a type is allowed to specify a concrete type, which is why impl Foo<u8> for Bar
makes sense. You could get back into the original situation by declaring a generic called u8
on a trait too!
impl<u8> Foo<u8> for Bar // Right back where we started
There's another, rarer case: you didn't mean for the type to be generic in any fashion in the first place! If that's the case, rename or remove the generic type declaration and write a standard function. For our example, if we always wanted to return a u8
, regardless of what type was passed in, we'd just write that:
trait Foo {
fn foo<T>(&self, value: T) -> u8;
}
impl Foo for Bar {
fn foo<T>(&self, value: T) -> u8 {
42
}
}
Good news! At least as of Rust 1.17, introducing this type of error is a bit easier to spot thanks to a warning:
warning: type parameter `u8` should have a camel case name such as `U8`