Returning a calculation result of local variable references

I'm trying to declare a struct with generics that implements some custom calculations. Generic type T might be a primitive or another bit large struct those implements operator overloads.

If the function calc has a single operation like below, it is compiled with no problems.

struct MyNumber<T> {
    a: T,
    b: T,
}

impl<'a, T> MyNumber<T> {
    pub fn calc(&'a self, other: &'a Self) -> T where &'a T: Add<&'a T, Output = T> {
        &self.a + &other.a
    }
}

But if the function has multiple operation with temporary variables, the compiler claims an error.

struct MyNumber<T> {
    a: T,
    b: T,
}

impl<'a, T> MyNumber<T> {
    pub fn calc(&'a self, other: &'a Self) -> T where &'a T: Mul<&'a T, Output = T> + Add<&'a T, Output = T> {
        let t1 = &self.a * &other.a;
        let t2 = &self.b * &other.b;
        &t1 + &t2
    }
}

Error message is:

error[E0597]: `t1` does not live long enough
  --> src/lib.rs:15:9
   |
11 | impl<'a, T> MyNumber<T> {
   |      -- lifetime `'a` defined here
12 |     pub fn calc(&'a self, other: &'a Self) -> T where &'a T: Mul<&'a T, Output = T> + Add<&'a T, Output = T> {
13 |         let t1 = &self.a * &other.a;
   |             -- binding `t1` declared here
14 |         let t2 = &self.b * &other.b;
15 |         &t1 + &t2
   |         ^^^------
   |         |
   |         borrowed value does not live long enough
   |         requires that `t1` is borrowed for `'a`
16 |     }
   |     - `t1` dropped here while still borrowed

I can't understand what these messages mean because it seems references of t1 and t2 makes a new variable (T) then t1 and t2 are no longer needed at the end of function.

Why this code is invalid? And how should I fix it?

You can use an HRTB to make &'a T implement Add<'a, T> and Mul<'a, T> for any lifetime 'a, not just a specific one:

use std::ops::{Add, Mul};

struct MyNumber<T> {
    a: T,
    b: T,
}

impl<T> MyNumber<T> {
    pub fn calc(&self, other: &Self) -> T
    where
        for<'a> &'a T: Mul<&'a T, Output = T> + Add<&'a T, Output = T>,
    {
        let t1 = &self.a * &other.a;
        let t2 = &self.b * &other.b;
        &t1 + &t2
    }
}

Playground.

So the question that remains for me is the following. Is the precise reason the error appears because of the Add bound

fn calc(&'a self, other: &'a Self) -> T 
where 
    &'a T: Add<&'a T, Output = T>

which prescribes that &t1 and &t2 have the same lifetime as self and other (i.e. 'a)? And then they cannot have the same lifetime 'a since they get created later? Or is it because of drop order? I.e. t1 and t2 don't live long enough because they get dropped before self and other?

The former is the problem. Local variables live in the unnamable liveness scope of the function that is shorter than any 'a the caller could pass as a generic lifetime argument. Drop order doesn't matter because &t1 + &t2 returns an owned T according to <&T as Add<&T, Output=T>::Output, which is then returned and thus moved from the scope of calc without being dropped.

1 Like