Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does using "Self" as a parameter type raise a lifetime error?

I'm currently following along with https://raytracing.github.io/books/RayTracingInOneWeekend.html but I'm implementing everything in Rust. Here's a excerpt from my vector implementation:

type Scalar = f64;

#[derive(Debug, Default, Clone)]
pub struct Vector {
    x: Scalar,
    y: Scalar,
    z: Scalar,
}

impl Vector {
    fn new(x: Scalar, y: Scalar, z: Scalar) -> Self {
        Self { x, y, z }
    }

    fn x(&self) -> Scalar {
        self.x
    }

    fn y(&self) -> Scalar {
        self.y
    }

    fn z(&self) -> Scalar {
        self.z
    }
}

impl std::ops::Mul<&Vector> for &Vector {
    type Output = Scalar;

    fn mul(self, rhs: Self) -> Self::Output {
        self.x() * rhs.x() + self.y() * rhs.y() + self.z() * rhs.z()
    }
}

When I try to compile it, I get the following message:

error[E0308]: method not compatible with trait
  --> src/point.rs:33:5
   |
33 |     fn mul(self, rhs: Self) -> Self::Output {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
   |
   = note: expected fn pointer `fn(&point::Vector, &point::Vector) -> _`
              found fn pointer `fn(&point::Vector, &point::Vector) -> _`
note: the lifetime `'_` as defined on the impl at 30:20...
  --> src/point.rs:30:20
   |
30 | impl std::ops::Mul<&Vector> for &Vector {
   |                    ^
note: ...does not necessarily outlive the lifetime `'_` as defined on the impl at 30:20
  --> src/point.rs:30:20
   |
30 | impl std::ops::Mul<&Vector> for &Vector {
   |                    ^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
error: could not compile `raytracing`.

However, if I change the Self parameter on the mul function to &Vector, it compiles just fine:

[...]
fn mul(self, rhs: &Vector) -> Self::Output {
[...]

Is this just a case where the lifetime inference fails? If so, why is it failing, since the compiler seems to have inferred everything correctly?

like image 246
Cesar Barata Avatar asked May 17 '20 15:05

Cesar Barata


1 Answers

It's because of rule of lifetime elision, the error message tell it:

note: the lifetime '_ as defined on the impl at 30:20...

The line:

impl std::ops::Mul<&Vector> for &Vector {

is interpreted as:

impl<'a, 'b> std::ops::Mul<&'a Vector> for &'b Vector // Self is &'b Vector

and so lifetime mismatch because:

fn mul(self, rhs: Self) -> Self::Output {

is

fn mul(self, rhs: &'b Vector) -> Self::Output {

&'a Vector != &'b Vector so it's can't compile. Cause rhs should be &'a Vector.

When you use Self:

impl std::ops::Mul<Self> for &Vector {

become:

impl<'a> std::ops::Mul<&'a Vector> for &'a Vector {

so in fn mul(self, rhs: Self) -> Self::Output { rhs will have the correct lifetime <'a>

If a lifetime problem occurs try to be explicit to check if compiler get it wrong.

The final code should not contain any Self keyword to allow different lifetime:

impl std::ops::Mul<&Vector> for &Vector {
    type Output = Scalar;

    fn mul(self, rhs: &Vector) -> Self::Output {
        self.x() * rhs.x() + self.y() * rhs.y() + self.z() * rhs.z()
    }
}

explicit:

impl<'a, 'b> std::ops::Mul<&'a Vector> for &'b Vector {
    type Output = Scalar;

    fn mul(self, rhs: &'a Vector) -> Self::Output {
        self.x() * rhs.x() + self.y() * rhs.y() + self.z() * rhs.z()
    }
}
like image 52
Stargateur Avatar answered Oct 25 '22 00:10

Stargateur