Lifetime error when there should be none

Of the following two snippets one gives a lifetime error and the other one does not.

Correct code : This uses a Vec<Vec<T1>> to store the iou_matrix output. T1 is a floating point number while T2 is an unsigned integer -- both expressed in terms of generics and implemented for regular types (f32,f64 etc for T1 and u8,u16 etc for T2).

use examiner_core::types;
use nalgebra;

use crate::bboxes;
pub trait DetectionEvaluator2D<T1 : types::FloatingPointNumber, T2 : types::UnsignedInteger, T3 : bboxes::IsBoundingBox2D<T1,T2>,  const NORMALIZED : bool>{
    fn iou_matrix(&self, gt : &impl bboxes::IsBoxCollection2D<T1,T2, T3, NORMALIZED>, det : &impl bboxes::IsBoxCollection2D<T1,T2,T3, NORMALIZED>)->Vec<Vec<T1>>{
        let gt_boxes = gt.bounding_boxes();
        let det_boxes = det.bounding_boxes();
        let mut out = Vec::<Vec<T1>>::new();
        let mut index = 0;
        for det_box in det_boxes{
            for gt_box in gt_boxes{
                out[index].push(det_box.iou(&gt_box));
            }
            index+=1;
        }
        out
    }
}

Incorrect code : As soon as I try creating a matrix using nalgebra I start getting the error which is shown after the incorrect snippet. I fail to understand why the lifetime of T1 a primitive type is becoming a problem here.

use examiner_core::types;
use nalgebra;

use crate::bboxes;
pub trait DetectionEvaluator2D<T1 : types::FloatingPointNumber, T2 : types::UnsignedInteger, T3 : bboxes::IsBoundingBox2D<T1,T2>,  const NORMALIZED : bool>{
    fn iou_matrix(&self, gt : &impl bboxes::IsBoxCollection2D<T1,T2, T3, NORMALIZED>, det : &impl bboxes::IsBoxCollection2D<T1,T2,T3, NORMALIZED>)->nalgebra::DMatrix<T1>{
        let num_gt = gt.num_boxes();
        let num_det = det.num_boxes();
        let gt_boxes = gt.bounding_boxes();
        let det_boxes = det.bounding_boxes();
        nalgebra::DMatrix::from_fn(num_det, num_gt, |r,c|->T1  {det_boxes[r].iou(&gt_boxes[c])})
    }
}

With the second snippet the error is as follows :

error[E0310]: the parameter type `T1` may not live long enough
  --> examiner_objdetect/src/protocols.rs:11:9
   |
11 |         nalgebra::DMatrix::from_fn(num_det, num_gt, |r,c|->T1  {det_boxes[r].iou(&gt_boxes[c])})
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T1` will meet its required lifetime bounds
   |
help: consider adding an explicit lifetime bound...
   |
5  | pub trait DetectionEvaluator2D<T1 : types::FloatingPointNumber + 'static, T2 : types::UnsignedInteger, T3 : bboxes::IsBoundingBox2D<T1,T2>,  const NORMALIZED : bool>{

I would like to understand where is this error originating from and why. I looked for other similar questions but I could not find to the best of my abilities a similar situation.

You know that T1 is a floating point number, but Rust doesn't. It sees a generic type T1 that does not satisfy the 'static bound, required by the interface of nalgebra. It is as simple as the compiler suggests, simply add the 'static bound to T1: T1: types::FloatingPointNumber + 'static. Now Rust knows that you will never pass a type as T1 that does not fulfill 'static. Non-static references or types with fields that contain non-static references don't fulfill the 'static bound. Floating point numbers do, but you have to tell the compiler that.

1 Like

I checked with 'static and it does work indeed.

Could you kindly shed a little light on why nalgebra requires T1 to be 'static ? Is this a design defect or is there a necessity to keep such an interface ?

Sure, bear with me here though as this might become a little convoluted. :smile:

You use this function Matrix::from_fn. It is defined in this impl block where T has the bound T: Scalar. If you look at the Scalar trait, you'll see that it has the 'static requirement for every type that implements it. Scalar is implemented for all types that fulfill the supertraits and 'static (including your T1: types::FloatingPointNumber + 'static).

Maybe you can add it as a bound directly to types::FloatingPointNumber? As you already said, floats and doubles are always 'static. Or replace types::FloatingPointNumber with Scalar as your bound on T1.

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.