Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add trait bound to a non-generic type?

I have this simple generic function:

fn add_five<T: Into<i32>>(t: T) -> i32 {
    5_i32 + t.into()
}

Which I would like to express using the From trait instead of the Into trait, however my attempted refactor:

fn add_five<T, i32: From<T>>(t: T) -> i32 {
    5_i32 + <i32 as From<T>>::from(t)
}

Throws this compile error:

error[E0277]: cannot add `i32` to `i32`
  --> src/main.rs:24:11
   |
   |     5_i32 + <i32 as From<T>>::from(t)
   |           ^ no implementation for `i32 + i32`
   |
   = help: the trait `Add<i32>` is not implemented for `i32`

Which is very confusing, because there is indeed an impl Add<i32> for i32 in the standard library, so what's the real problem?

like image 929
pretzelhammer Avatar asked Feb 06 '21 21:02

pretzelhammer


1 Answers

Adding a trait bound to a non-generic type can only be done from the where clause:

fn add_five<T>(t: T) -> i32
where
    i32: From<T> // now compiles!
{
    5_i32 + <i32 as From<T>>::from(t)
}

The reason <T, i32: From<T>> fails is because the compiler parses all of the names used within <> as identifiers for generic type parameters.

The error message is confusing because the compiler doesn't clarify when i32 refers to the concrete 32-bit signed integer type vs the function-local identifier for a generic type parameter by the same name (which also now shadows the concrete integer type).

Here's the error message but with added clarifications:

error[E0277]: cannot add `i32` (generic type) to `i32` (concrete integer type)
  --> src/main.rs:24:11
   |
   |     5_i32 + <i32 (generic type) as From<T>>::from(t)
   |           ^ no implementation for `i32 (concrete integer type) + i32 (generic type)`
   |
   = help: the trait `Add<i32 (generic type)>` is not implemented for `i32` (concrete integer type)

Or to substitute the confusingly ambiguous i32 for the more conventional U as the generic type:

error[E0277]: cannot add `U` to `i32`
  --> src/main.rs:24:11
   |
   |     5_i32 + <U as From<T>>::from(t)
   |           ^ no implementation for `i32 + U`
   |
   = help: the trait `Add<U>` is not implemented for `i32`

The fix is to simply move the bound into the where clause, as stated above, to avoid accidentally declaring i32 as a generic type.

like image 183
pretzelhammer Avatar answered Oct 06 '22 13:10

pretzelhammer