Even without any trait bounds, a signature like
fn foo<T>(x: &T, y: &T, z: &T) -> … {}
would always mean “three parameters that are references to the same type T
, which can be any type chosen by the caller of the function”.
Additional where
bounds only further restrict the possibilities for the caller, so this interpretation of “when I write the same T
multiple times, it always means the same type” never goes away.
Rust also has a convenience syntax for introducing unnamed type parameters with a trait bound directly inside of the signature, the impl Trait
syntax. (Note that impl Trait
in return types is very different.)
In function arguments, each occurrence of impl Trait
will introduce a new unnamed type parameter, so you can make use of that and something like
fn turn_direction(p1: &impl PointLike, p2: &impl PointLike, p3: &impl PointLike) -> isize
{..}
is equivalent to
fn turn_direction_flex<T, U, V>(p1: &T, p2: &U, p3: &V) -> isize
where
T: PointLike,
U: PointLike,
V: PointLike,
{ .. }
except that when you use this short-hand, you can no longer explicitly via “turbofish” specify these types in a
turn_direction_flex::<FirstType, SecondType, ThirdType>(first_arg, second_arg, thirg_arg)
style of call.
Unfortunately we do not have a way to easily reduce the repetition here, which can be a problem in case the trait bounds become very long. For your example code where it’s just “PointLike
”, that isn’t an issue of course, because that’s just a single trait without parameters any anything. While there are some workarounds for creating something trait aliases, the real thing as a “trait aliases” language feature doesn’t exist yet in stable Rust. (This feature is useful beyond the case of multiple arguments of a single function sharing a long and complicated trait bound. It can help just as well with the problem of many different functions needing to repeat the same long and complicated trait bound.)