I\'m trying to write a function like the following in Rust:
fn double_and_square<\'a, T>(x: &\'a T) -> /* whatever the output type of `&t *
Buckle up...
use std::ops::{Add, Mul};
fn double_and_square<'a, T, R>(x: &'a T) -> R
where
&'a T: Add,
for<'b> &'b <&'a T as Add>::Output: Mul<Output = R>,
{
let t = x + x;
&t * &t
}
Easy enough, right? ;-)
Let's take it step by step...
You wish to take in a reference to a type, but the reference needs to implement Add
. where
clauses let you write complex types on either side of the :
, so we use &'a T: Add
.
This will return some value that we take another reference to. However, the caller of double_and_square
cannot specify the lifetime, since it only exists inside the function. This means we need to use a higher-ranked trait bound: for <'b>
.
We have to use the type of the output of the Add
operation, say that it implements Mul
, and the output type is the generic R
.
I'd recommend not taking references in the original function as it's way easier to understand:
fn double_and_square<T, R>(x: T) -> R
where
T: Add + Copy,
for<'a> &'a T::Output: Mul<Output = R>,
{
let t = x + x;
&t * &t
}
&Foo
is a separate type from Foo
and can be passed as the concrete type of T
, so this should be able to be used in any place the original was, and probably usable in even more cases.
I want it to work on types where
T
is non-Copy
Immutable references to types are always Copy
, even if the type itself doesn't implement Copy
. Thus, you can call this function with e.g. T = i32
or a T = &NonCopy
. The original case that only accepted references would only accept the second one.
In an ideal world, you'd be able to avoid the generic type R
and just say <...something...>::Output
, but as far as I know that's not currently possible.