Confusing compiler message for specialization impl of generic traits


#1

Hi everyone:

I am playing with generic traits and specialization implementation. The following example works fine, and seems to be the correct syntax:

struct Sys {
}

trait F<T> {
    fn f(self) -> T;
}

// Specialization impl
impl F<i32> for Sys {
    fn f(self) -> i32 {
        3i32
    }
}

fn main() {
    let s = Sys{};
    
    assert_eq!(3i32, s.f());
}

I somehow tried to write this fragment this other way (the impl is different):

struct Sys {
}

trait F<T> {
    fn f(self) -> T;
}

// Generics would be impl<T> F<T> for Sys
// but specializations are just impl F<i32>
impl<i32> F<i32> for Sys {
    fn f(self) -> i32 {
        3i32
    }
}

fn main() {
    let s = Sys{};
    
    assert_eq!(3i32, s.f());
}

and I get the following surprising compiler error:

error: mismatched types [--explain E0308]
  --> <anon>:10:9
10 |>         3i32
   |>         ^^^^ expected type parameter, found i32
note: expected type `i32`
note:    found type `i32`

error: aborting due to previous error

I think this is a syntax error, but the compiler does not report it so. Am I missing something here? Is the compiler failing?


#2

I guess you problem may be the same as this one https://github.com/rust-lang/rust/issues/35030


#3

Indeed! I searched for this issue before posting but didn’t find this message. Thanks for the pointer.


#4

To explain the error, there’s no syntax error here. Type parameters are most often given a single uppercase letter like T as a name, but they can be given any name that would be valid as the name of a type. This includes shadowing the names of other types, like primitives. Inside that impl block the type i32 is an type parameter with no trait bounds, not the primitive i32.

This is confusing, but I’m not sure of a better way to fix it than just to disallow shadowing in-scope types. This would be a breaking change, but I’m not sure what use it has other than for writing obfuscated code. For example, this compiles (with a warning about the variable name being camel case):

fn stringify<String: ToString>(String: String) -> ::std::string::String {
    String.to_string()
}