Why it is not needed to add <f64, f64> after impl?

The struct is written like this:

struct Point<T, U> {
    x: T,
    y: U,
}

When we define generic method for a generic struct, we write it like this:

impl<T, U> Point<T, U> {
    fn x(&self) -> &T{
        &self.x
    }
    fn y(&self) -> &U{
        &self.y
    }
}

But when I want a specific method for only type f64, I write this:

impl Point<f64, f64> {
    fn multiply(&self) ->f64 {
        &self.x*&self.y
    }
}

I am just wondering, why the first line is not like this?
impl<f64, f64> Point<f64, f64> {

Why do we need <T, U> following the impl up there? Thanks.

f64 is a concrete type. T is not — it can be anything.

1 Like

The first pair of angle brackets are for declaring type variables, to distinguish them from actual types of the same name. For example:

struct T;
struct S<X>(X);

impl S<T> {
    // T here refers to struct T above
}

impl<T> S<T> {
    // T here is a variable that can be replaced with any type
}
5 Likes

impl<T, U> declares type variables named T and U. If you want to use fixed type instead you don't need to declare variables. For examples with closures you may write code like this:

// prints whatever passed
|a, b| println!("{} {}", a, b)
// prints fixed values
|| println!("{} {}", 2, 3)

but not like this:

|2, 3| println!("{} {}", 2, 3)
8 Likes

Thanks,
but do you feel the first <T, U> is a little bit redundant? Considering the second <T, U> already told the compiler this is gonna be a generic type.
impl<T, U> Point<T, U> {

They are only redundant in simple cases. For example, if I have a trait Foo and a type Bar that both have type parameters then each of these is valid and means something different:

impl<T> Foo<T> for Bar<f64> {}
impl<T> Foo<f64> for Bar<T> {}
impl<T> Foo<T> for Bar<T> {}
impl Foo<f64> for Bar<f64> {}
impl<T> Bar<Option<T>> {}

In all cases, the compiler needs to know that T is a type variable, because if we didn't tell it so explicitly, there would be no way to distinguish an intended type variable from a concrete type missing a use, and that would be potentially disastrous either for the program doing what was meant, or for the programmer's understanding of what's wrong.

2 Likes

Thanks!