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

1

8

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?

Selestina answered 17/5, 2020 at 15:49 Comment(0)
B
5

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()
    }
}
Baryton answered 17/5, 2020 at 19:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.