9

I'd like to make the following code compile:

struct Provider {}

impl Provider {
    fn get_string<'a>(&'a self) -> &'a str { "this is a string" }
}

fn main() {
    let provider = Provider{};
    let mut vec: Vec<&str> = Vec::new();

    // PROBLEM: how do I say that this reference s here
    // needs to live as long as vec?
    let fun = |s: &str| {
        vec.push(s);
    };

    fun(provider.get_string());
}

Playground link

This is the compile error that I get:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
 --> src/main.rs:9:22
  |
9 |     let mut vec: Vec<&str> = Vec::new();
  |                      ^^^^
  |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the block at 11:24...
 --> src/main.rs:11:25
  |
11|     let fun = |s: &str| {
  |                         ^
note: ...so that reference does not outlive borrowed content
 --> src/main.rs:12:18
  |
12|         vec.push(s);
  |                  ^
note: but, the lifetime must be valid for the block suffix following statement 2 at 13:6...
 --> src/main.rs:13:7
  |
13|     };
  |       ^
note: ...so that variable is valid at time of its declaration
 --> src/main.rs:11:9
  |
11|     let fun = |s: &str| {
  |         ^^^
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
bennofs
  • 11,873
  • 1
  • 38
  • 62
  • Uh! That's a fun one. Sometimes I really wish I could use `'provider` to say "the lifetime of that guy, there". – Matthieu M. Jan 25 '17 at 11:36
  • not sure how to make it work. As a workaround, maybe you could let the closure capture s from the environment too instead of passing it in as a parameter. Like [this](https://play.rust-lang.org/?gist=e7fd2f4ee7c58b9e592a1f060b2d396b&version=stable&backtrace=0) – Paolo Falabella Jan 25 '17 at 12:44
  • @Shepmaster I've since found that as well. That is the solution that also works for the real problem :) Still, I'm quite surprised that's impossible to manually specify the type of `s` here. – bennofs Jan 25 '17 at 14:40

2 Answers2

8

Your code works just fine if remove all the lifetime annotations and let the compiler inference do its job:

struct Provider;

impl Provider {
    fn get_string(&self) -> &str { "this is a string" }
}

fn main() {
    let provider = Provider;
    let mut vec = Vec::new();

    let mut fun = |s| {
        vec.push(s);
    };

    fun(provider.get_string());
}

In short, there's no way to explicitly refer to the lifetime of a local variable, only function arguments. The compiler knows how to do it, though.

If you truly needed it, you could create a function to allow annotating the lifetimes:

fn thing<'a>(provider: &'a Provider) -> Vec<&'a str> {
    let mut vec: Vec<&'a str> = Vec::new();

    {
        let mut fun = |s: &'a str| vec.push(s);

        fun(provider.get_string());
    } // End mutable borrow of `vec`

    vec
}

fn main() {
    let provider = Provider;
    thing(&provider);
}

why did the original annotations stop things from working?

Specifically, it's this bit:

let fun = |s: &str| {
    vec.push(s);
};

This declares a new lifetime on the closure. Using a made-up syntax (you can't declare lifetimes on closure arguments), it would be equivalent to:

let fun = <'a> |s: &'a str| {
    vec.push(s);
};

Which is why the compiler has the error:

the lifetime cannot outlive the anonymous lifetime #1 defined on [the closure's block]

There's no connection between that generated lifetime and that of the Provider. Leaving it out allows the compiler to insert the desired but unnamable lifetime.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Is there any work/proposal to make it possible to specify the type of `s` explictly in a future rust version? I guess what would be needed for that is some kind lifetime specification that says that the reference has the same lifetime as , so I could write `|s: &'liftime_of(provider) str` – bennofs Jan 25 '17 at 14:45
  • @bennofs I know of no such way or proposal to allow explicitly referring to the lifetime of a local variable. – Shepmaster Jan 25 '17 at 14:52
  • today I learned that inference can be more powerful (vs just more convenient) than manually specifying lifetimes... Thanks for that! – Paolo Falabella Jan 25 '17 at 15:45
  • 1
    @PaoloFalabella it was pretty confusing to me at first, because I always strive to remove any explicit lifetimes / types. At some point, I hit strange problems because a question asker had explicit lifetimes and I didn't, so I couldn't figure out what was broken. Now I remember. – Shepmaster Jan 25 '17 at 15:47
  • Can you explain why the original annotations stopped things from working? None of them look different to me to what I'd expect the compiler to de-elide or infer. – Chris Emerson Jan 25 '17 at 16:08
3

Here's a version which compiles:

use std::marker::PhantomData;

struct Provider<'a> {
    _dummy: PhantomData<&'a ()>,
}

impl<'a> Provider<'a> {
    fn get_string(&self) -> &'a str {
        "this is a string"
    }
}

fn f<'b>() {
    let provider = Provider { _dummy: PhantomData };
    let mut vec: Vec<&str> = Vec::new();

    // PROBLEM: how do I say that this reference s here
    // needs to live as long as vec?
    let mut fun = |s: &'b str| { vec.push(s); };

    fun(provider.get_string());
}

fn main() {
    f()
}

Playground link

I made the following changes:

  • Add a lifetime to Provider (I added a PhantomData, but I guess your provider already owns some data it'll provide).
  • Update the get_string method to show that it returns something with the provider's lifetime, not the input lifetime (ie based on the Provider's lifetime parameter).
  • Add a new lifetime parameter 'b to the function (which I renamed to f(), since main() can't have one), which I use to name the lifetime of the closure parameter.

The last one is slightly confusing, as apparently merely adding a name to a lifetime (without apparently adding any constraints) has made it work.

I think (but I'd love some documentation for this) that this is because of lifetime elision. A closure is really a hidden struct with a fn call(&self, s: &str) (in this case) method. According to the lifetime elision rules, the s parameter gains the same lifetime as &self, which is the closure itself. In this case, the closure is declared after vec, so the lifetime is too short. The explicit lifetime means that it is decoupled from the closure's own lifetime.

Chris Emerson
  • 13,041
  • 3
  • 44
  • 66
  • This answer is certainly interesting and provided me some new infos about lifetimes, but unfortunately, in my problem, `Provider` is an `Arena` which does not have any data that it owns :( So the reference really does not live any longer than the provider itself. – bennofs Jan 25 '17 at 13:26
  • What determins how long the result of `get_string` lives in your case? – Chris Emerson Jan 25 '17 at 13:28
  • It lives exactly as long as the `provider` does – bennofs Jan 25 '17 at 13:31
  • In which case does the `PhantomData` not do what you want as in the answer? – Chris Emerson Jan 25 '17 at 13:33
  • Consider the case where the `String` owned by `Provider` (through an attribute for example). Example (edited wrong link): https://play.rust-lang.org/?gist=4c97845847bbda978b41f30b26a0f7db&version=stable&backtrace=0 – bennofs Jan 25 '17 at 13:36