Help of nalgebra matrix slices and lifetime

Hi,

I want to have a callback that produces nalgebra DMatrixSlice from a know fixed DMatrix. I have the following code snippet:


use nalgebra::*;

struct Compute;

impl<'a> Compute {
    fn run(&self, callback: Box<dyn Callback<'a>>) {
        let my_slice = (*callback).make_slice(10, 10);
        ()
    }
}


trait Callback<'a> {
    fn make_slice(&self, idx: usize, len: usize) -> DMatrixSlice<'a, f32>;
}

struct MyCallback {
    matrix: DMatrix<f32>
}

impl<'a> Callback<'a> for MyCallback {
    fn make_slice(&self, idx: usize, len: usize) -> DMatrixSlice<'a, f32> {
        self.matrix.slice((0, 0), (2, 2))
    }
}

fn main() {

    let mat = DMatrix::from_vec(3, 3, vec![1f32, 2f32, 3f32, 4f32, 5f32, 6f32, 7f32, 8f32, 9f32]);

    let c = Compute{};

    c.run(Box::new(MyCallback{
        matrix: mat
    }));

}

This yields the following errors:

/Users/mariusdanciu/.cargo/bin/cargo run --color=always --package nn --bin example
   Compiling nn v0.1.0 (/Users/mariusdanciu/work/dev/projects/nn)
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/neunet/example.rs:23:21
   |
23 |         self.matrix.slice((0, 0), (2, 2))
   |                     ^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 22:5...
  --> src/neunet/example.rs:22:5
   |
22 | /     fn make_slice(&self, idx: usize, len: usize) -> DMatrixSlice<'a, f32> {
23 | |         self.matrix.slice((0, 0), (2, 2))
24 | |     }
   | |_____^
note: ...so that reference does not outlive borrowed content
  --> src/neunet/example.rs:23:9
   |
23 |         self.matrix.slice((0, 0), (2, 2))
   |         ^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 21:6...
  --> src/neunet/example.rs:21:6
   |
21 | impl<'a> Callback<'a> for MyCallback {
   |      ^^
   = note: ...so that the expression is assignable:
           expected nalgebra::base::matrix::Matrix<_, _, _, nalgebra::base::matrix_slice::SliceStorage<'a, _, _, _, _, _>>
              found nalgebra::base::matrix::Matrix<_, _, _, nalgebra::base::matrix_slice::SliceStorage<'_, _, _, _, _, _>>

error: aborting due to previous error

error: Could not compile `nn`.

To learn more, run the command again with --verbose.

Process finished with exit code 101

Any suggestions? I also tried with closures and cannot pass these types of errors.

Appreciate your help,
Marius

Don't annotate lifetimes.

trait Callback {
    fn make_slice(&self, idx: usize, len: usize) -> DMatrixSlice<'_, f32>;
}

struct MyCallback {
    matrix: DMatrix<f32>
}

impl Callback for MyCallback {
    fn make_slice(&self, idx: usize, len: usize) -> DMatrixSlice<'_, f32> {
        self.matrix.slice((0, 0), (2, 2))
    }
}
2 Likes

Thank you so much ! It works perfectly .... but so I have a better understanding what is the reason behind it ?

You were threading a single lifetime from Compute all the way to DMatrixSlice. Which tells the compiler that the slice needs to live at least as long as Compute. In this example, the slice only needs to live within run. Lifetime inference can pick an appropriate lifetime.

2 Likes

Got it. Makes sense ! Thank you !

Well, that's not quite it, Compute is actually not relevant. Let's walk through the code

use nalgebra::*;

struct Compute;

// 'a here is chosen by whoever uses the trait `Callback` as a trait bound
// or a trait object
trait Callback<'a> {
    // so when you use 'a here it could be *any* lifetime
    // including, say 'static
    // most notably, it has no relationship to the lifetime of `self`
    // this means that you can't return a reference into the contents of `self`
    // (like a reference to `matrix` in `MyCallback`)
    fn make_slice(&self, idx: usize, len: usize) -> DMatrixSlice<'a, f32>;
}

struct MyCallback {
    matrix: DMatrix<f32>
}

// declare the lifetime 'a, not related to anything else
impl<'a> Callback<'a> for MyCallback {
    // note how 'a is only mentioned in the return type
    // because of this, Rust thinks that it's not related to any other parameter
    // and since the caller can choose the lifetime 'a, it could outlive self
    // so Rust can't prove that a borrow into `self`
    // will be valid for the lifetime 'a
    fn make_slice(&self, idx: usize, len: usize) -> DMatrixSlice<'a, f32> {
        self.matrix.slice((0, 0), (2, 2))
    }
}

Now let's compare to no annotations at all

use nalgebra::*;

struct Compute;

trait Callback {
    // Here because of something called lifetime elision, '_ get's inferred to be
    // the same lifetime as `&self`
    fn make_slice(&self, idx: usize, len: usize) -> DMatrixSlice<'_, f32>;

//  like this
//  fn make_slice<'a>(&'a self, idx: usize, len: usize) -> DMatrixSlice<'a, f32>;
}

struct MyCallback {
    matrix: DMatrix<f32>
}

impl Callback for MyCallback {
    // so here Rust can prove that a borrow into `self`
    // will be valid for the lifetime '_, because '_ *is* that lifetime!
    fn make_slice(&self, idx: usize, len: usize) -> DMatrixSlice<'_, f32> {
        self.matrix.slice((0, 0), (2, 2))
    }
}
4 Likes

Thank you very much for the detailed response. I am pretty new to Rust (really like it so far) and this is a good lesson for me.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.