If we line up the expressions in the order of operations...[1]
a + (b - a) * t
// ^ ^ &'a T - &'a T = T (&'a T: Sub<&'a T, Output = T>)
// ^^^^^^^ ^ T * f32 = T (T: Mul<f32, Output = T>)
// ^ ^^^^^^^^^^^ &'a T + T = T (&'a T: Add<T, Output = T>)
It would be a very unusual numeric type which both implements Copy and which is large enough in memory that copying it is more expensive than accessing memory through a pointer.
(But in most cases, a function like this is a candidate for inlining, and once inlined, the reference vs. copy distinction is more or less entirely gone â the optimizer will transform the code to whatever it thinks is the most efficient form.)
Considering that I must create a temporary value anyway due to the requirement of the arithmetic operators, I guess there's no avoiding the copies. Going forward, should I just implement Copy on these kinds of types then?
Don't guess; look at godbolt and see. Do you have a runable example of the code?
And remember that copying an f32 is half the cost of passing a reference, on a 64-bit machine. Anything the size of two pointers or smaller is almost always better to just pass a copy rather than bothering indirecting through memory.
The optimizer is great. Even things like calling array::from_fn to make a new array, going through a closure and such, ends up optimizing down to just a SIMD operation anyway https://rust.godbolt.org/z/Y1hbWPTMq, for example. That also shows that passing it owned or by-reference doesn't matter in that case -- LLVM ends up turning them into the exact same code on the default x64 target.