0
votes

This code compiles fine.

Foo contains a reference to an object of type X. It has a simple function new() which does this. Then there's a function called foo2() (it can also be a free function outside the impl block) which accepts a reference to X and returns a completely new object of type Foo. This works fine.

The problem is if I uncomment foo() which includes the parameter &self. Suddenly it doesn't work now. Why? If I add lifetimes then it works, but why can it not figure this out? Thanks.

struct X {
}

struct Foo<'a> {
    x: &'a X
}

impl<'a> Foo<'a> {
    fn new(x: &'a X) -> Self {
        Self {
            x
        }
    }
}

struct B {
}

impl B {
    // This doesn't work
    /*fn foo(&self, x: &X) -> Foo {
        Foo::new(x)
    }*/

    // This does work
    fn foo2(x: &X) -> Foo {
        Foo::new(x)
    }
}

fn main() {
    let x = X {};
    let b = B {};
    //let f = b.foo(&x);
}

But when I uncomment foo() then I get this error:

error[E0623]: lifetime mismatch
  --> main.rs:21:9
   |
20 |     fn foo<'a>(&self, x: &'a X) -> Foo {
   |                          -----     ---
   |                          |
   |                          this parameter and the return type are declared with different
 lifetimes...
21 |         Foo::new(x)
   |         ^^^^^^^^^^^ ...but data from `x` is returned here

Is there some kind of ellison happening here which &self messes up? What's going on here? Thanks

This works:

fn foo<'a>(&self, x: &'a X) -> Foo<'a> {
    Foo::new(x)
}

But why doesn't the compiler figure this out?

1
You seem to think that the compiler should deduce the ellision rules from the code of your function (just one line of code, how hard can it be?). But the Rust ellision rules must be executed looking only at the function declaration, not the body. The alternative would be that the semantics of your public function could change subtly by changes in its implementation, and that would be quite messy. - rodrigo

1 Answers

2
votes

The Rust Reference -> Lifetime elision

If the receiver has type &Self or &mut Self, then the lifetime of that reference to Self is assigned to all elided output lifetime parameters.

There is a reason why compiler try to pickup lifeteime from &self and not from x: &X.