This parameter and the return type are declared with different lifetimes

Hello everybody, rust beginners here. I am writing a method for a class Ray called intersect, that given the starting point and ending point of a segment on a 2D plane, finds if there is an intersection or not.
The method intersect works like this:

    let start_line = vec2(300.0, 100.0);
    let end_line = vec2(300.0, -300.0);
    let mut r = Ray2D::new();
    // ...
    if let Some(collision) = r.intersect(start_line.x, start_line.y, end_line.x, end_line.y) {
        draw.ellipse().color(GREEN).x_y(collision.x, collision.y);
    };

This is my Ray2D code:

#[allow(dead_code)]
use nannou::prelude::*;

pub struct Ray2D {
    orig: Vector2,
    dir: Vector2,
}

impl Ray2D {
    pub fn new() -> Self {
        Ray2D {
            orig: vec2(0.0, 0.0),
            dir: vec2(1.0, 0.0),
        }
    }

    // ....

    pub fn intersect(&self, x1: f32, y1: f32, x2: f32, y2: f32) -> Option<Vector2> {
        let x3 = self.orig.x;
        let y3 = self.orig.y;
        let x4 = self.orig.x + self.dir.x;
        let y4 = self.orig.y + self.dir.y;
        let den = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);

        let tri = (
            den,
            ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / den,
            -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / den,
        );

        match tri {
            (d, t, u) if d != 0.0 && t > 0.0 && t < 1.0 && u > 0.0 => {
                Some(vec2(x1 + t * (x2 - x1), y1 + t * (y2 - y1)))
            }
            _ => None,
        }
    }

    pub fn intersect_reuse(
        &self,
        x1: f32,
        y1: f32,
        x2: f32,
        y2: f32,
        collision: &mut Vector2,
    ) -> Option<&mut Vector2> {
        let x3 = self.orig.x;
        let y3 = self.orig.y;
        let x4 = self.orig.x + self.dir.x;
        let y4 = self.orig.y + self.dir.y;
        let den = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);

        let tri = (
            den,
            ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / den,
            -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / den,
        );

        match tri {
            (d, t, u) if d != 0.0 && t > 0.0 && t < 1.0 && u > 0.0 => {
                collision.x = x1 + t * (x2 - x1);
                collision.y = y1 + t * (y2 - y1);
                Some(collision)
            }
            _ => None,
        }
    }
}

What at the moment I am not able to di, is to pass as mutable reference a 2dVector to the intersect_reuse method and to have its x and y component updated if there is an intersection. I have tried an implementation in the intersect_result method but I get this error message:

error[E0623]: lifetime mismatch
  --> ray2d/src/lib.rs:75:17
   |
57 |         collision: &mut Vector2,
   |                    ------------ this parameter and the return type are declared with different lifetimes...
58 |     ) -> Option<&mut Vector2> {
   |          --------------------
...
75 |                 Some(collision)
   |                 ^^^^^^^^^^^^^^^ ...but data from `collision` is returned here

error: aborting due to previous error

I have read the chapter of the book regarding lifetime, Validating References with Lifetimes - The Rust Programming Language, but I do not know how to solve this.

Maybe someone has a suggestion?

It looks like you need to add lifetime annotations to your function signature.

    pub fn intersect_reuse<'a>(
        &self,
        x1: f32,
        y1: f32,
        x2: f32,
        y2: f32,
        collision: &'a mut Vector2,
    ) -> Option<&'a mut Vector2> { ... }

This says that the vector you're returning has the same lifetime as the vector you're receiving.

Note however that returning a reference to the vector that you took as an argument is usually pointless, though I guess it depends on how you're calling the function.

Thanks Andrew, it is working. I am passing a reference because I have ~1000 rays and I want to draw the intersection with a group of ~ 60 segments. I am reusing the vector passed as argument in order to avoid to put a new vector on the stack for each collision. Does it make sense?

If, when trying to parallelize ray casting, I will hit some problems, like the reference that can not be share, I will simply use the intersect method.

Does it make sense?

Sure. If you want to reuse the same memory though you can just update collision and return a bool. That's not necessarily any better, I'm just pointing out that it will work.

Filed Suggest named lifetime on trait functions like we do for free functions · Issue #73931 · rust-lang/rust · GitHub

@edapx, would a suggestion like the one for functions have been enough to help you here?

Honestly, I do not enough enough experience to give you a valuable feedback regarding that issue.

If you had seen something like

help: consider introducing a named lifetime parameter
  |
1 | fn foo<'a>(x: &'a i32, y: &'a i32) -> Option<&'a i32> {
  |       ^^^^    ^^^^^^^     ^^^^^^^            ^^^

Would that have unblocked you?

It would have helped me, yes. But I think that also the sentence this parameter and the return type are declared with different lifetimes... is necessary to introduce the problem to a novice.

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.