Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I write the lifetimes for references in a type constraint when one of them is a local reference?

I have a trait Matrix and generic function semi_def<T: Matrix>(x: &T) that I would like to operate on that trait. The function requires an operator trait, say Mul, be implemented on T. However, I can't seem to make the lifetimes happy if one of the references is to a local variable. How do I write the lifetimes for references in the type constraint when one of them is just a local temporary reference?

use std::ops::Mul;

trait Matrix: Clone {
    fn transpose(self) -> Self;
}

#[derive(Clone)]
struct DenseMatrix {
    n_rows: usize,
    n_columns: usize,
    elements: Vec<f64>,
}

impl Matrix for DenseMatrix {
    fn transpose(self) -> Self {
        unimplemented!()
    }
}

impl<'a, 'b> Mul<&'b DenseMatrix> for &'a DenseMatrix {
    type Output = DenseMatrix;
    fn mul(self, _rhs: &'b DenseMatrix) -> Self::Output {
        unimplemented!()
    }
}

fn semi_def<'a, T: Matrix>(x: &'a T) -> T
where
    &'a T: Mul<&'a T, Output = T>,
{
    &(*x).clone().transpose() * x
}

fn main() {}

which gives this error:

error[E0597]: borrowed value does not live long enough
  --> src/main.rs:31:6
   |
31 |     &(*x).clone().transpose() * x
   |      ^^^^^^^^^^^^^^^^^^^^^^^^ temporary value does not live long enough
32 | }
   | - temporary value only lives until here
   |
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 27:1...
  --> src/main.rs:27:1
   |
27 | / fn semi_def<'a, T: Matrix>(x: &'a T) -> T
28 | | where
29 | |     &'a T: Mul<&'a T, Output = T>,
30 | | {
31 | |     &(*x).clone().transpose() * x
32 | | }
   | |_^
like image 981
drhagen Avatar asked Jun 03 '17 10:06

drhagen


1 Answers

You need higher-ranked trait bounds (HRTBs), which are described in the advanced Rust book Rustonomicon and well as on Stack Overflow. They allow a type constraint to say that trait must be implemented not just for references with a particular lifetime but for any lifetime. They use the where for<> syntax. Here is the function definition that says an implementation of Mul is needed for any two references to T:

fn semi_def<'a, T: Matrix>(x: &'a T) -> T
where
    for<'b, 'c> &'b T: Mul<&'c T, Output = T>,
{
    &(*x).clone().transpose() * x
}

Because one of the references actually has the lifetime 'a, not a local lifetime, this could be written with a slightly looser constraint:

fn semi_def<'a, T: Matrix>(x: &'a T) -> T
where
    for<'b> &'b T: Mul<&'a T, Output = T>,
{
    &(*x).clone().transpose() * x
}

This Q&A is based off a question I asked on the Rust users mailing, which I cleaned up and brought over here for future Rustaceans.

like image 82
drhagen Avatar answered Nov 15 '22 08:11

drhagen