The thing is that the product you wrote in the iterator used a borrow to a T
as both the left and right operands (and had to output an owned T
).
Hence the bound &T : Mul<Output = T>
.
But if you try writing that down it will complain about a missing lifetime parameter:
That happens because there is no such thing as the &T
type. Try writing type Ref<T> = &T;
and you will see that Rust will complain that the right hand side is not a type: it is missing a lifetime parameter.
The type of (shared) borrows over a value of type T
for the lifetime 'borrow
is &'borrow T
. That is an actual type.
- (if
T
itself was a borrow (e.g., T = &'inner String
), then for &'borrow &'inner String
to be valid, the lifetime 'inner
must include / be at least as big as 'borrow
. This constraint is written 'inner : 'borrow
, and more generally, for a generic T
, it is written T : 'borrow
.)
type Ref<'borrow, T> = &'borrow T;
- so the
&'_ _
type constructor has an arity of 2: a lifetime parameter, and a type parameter.
There are, however, many places where the lifetime may be elided, such as when annotating the types in a let
binding, in the arguments of a function, or in a function's return type. This is possible because there are very explicit "lifetime elision" rules that allow Rust to cut us some slack.
What may be surprising is that there are cases where there are real ambiguities,, and then Rust will force you to make the lifetime parameters explicit.
This is the case in trait bounds: when writing down &T : Mul...
, there are many different interpretations to the bound, depending on the value of the elided lifetime parameter.
For instance, three notable cases come to mind:
-
&'static T : Mul...
, i.e., only infinite borrows can be multiplied;
-
&'input T : Mul...
, i.e., only borrows borrowed for exactly the same lifetime as the input vec can be multiplied;
- Note that for
'input
or 'borrow
or 'a
to exist, it needs to be a generic parameter of the function (or the trait containing the function). If, moreover, you wish that that lifetime parameter refer to the borrow of the input Vec
, then the lifetime parameter of that argument can no longer be elided, since it must use the parameter explicitely. In my answer I had called it 'borrow
-
or all the references to a T
, no matter the lifetime, can be multiplied. This universal quantification over a parameter is called in rust Higher Rank Trait Bounds, and currently only supports unbounded lifetime parameters:
for<'x> &'x T : Mul<Output = T>
(This works for your function, but there was a bug that made the compilation crash when we monomorphised to a specific non-'static
type (I don't know if it has been fixed), so I try to avoid it when it is bot necessary).
As you can see, there is no obvious answer, so Rust wants the programmer to specify their wish.
A simpler solution to you code (but less generic) is to add a T : Copy
bound, and use a dereferencing pattern |&x|
as the argument of the closure. This way, no borrows (and thus no lifetimes!) are involved.