7

What is my error and how to fix it?

fn get_m() -> Vec<i8> {
    vec![1, 2, 3]
}

fn main() {
    let mut vals = get_m().iter().peekable();
    println!("Saw a {:?}", vals.peek());
}

(playground)

The compiler's error suggests "consider using a let binding" — but I already am:

error[E0597]: borrowed value does not live long enough
 --> src/main.rs:6:45
  |
6 |     let mut vals = get_m().iter().peekable();
  |                    -------                  ^ temporary value dropped here while still borrowed
  |                    |
  |                    temporary value created here
7 |     println!("Saw a {:?}", vals.peek());
8 | }
  | - temporary value needs to live until here
  |
  = note: consider using a `let` binding to increase its lifetime

This is obviously a newbie question -- though I thought I'd written enough Rust at this point that I had a handle on the borrow checker... apparently I haven't.

This question is similar to Using a `let` binding to increase value lifetime, but doesn't involve breaking down an expression into multiple statements, so I don't think the problem is identical.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Bosh
  • 8,138
  • 11
  • 51
  • 77
  • 3
    A hint on reading the error messages: “borrowed value does not live long enough”. Which borrowed value? Look at the span, it’s `get_m()`. “Consider using a let binding to increase its lifetime”. That is, use a let binding *on the `get_m()` part*. – Chris Morgan Mar 06 '15 at 06:34
  • Thanks @ChrisMorgan! The extent of the squiggles is a nice clue. – Bosh Mar 06 '15 at 22:01

2 Answers2

12

The problem is that the Peekable iterator lives to the end of the function, but it holds a reference to the vector returned by get_m, which only lasts as long as the statement containing that call.

There are actually a lot of things going on here, so let's take it step by step:

  • get_m allocates and returns a vector, of type Vec<i8>.
  • We make the call .iter(). Surprisingly, Vec<i8> has no iter method, nor does it implement any trait that has one. So there are three sub-steps here:
    • Any method call checks whether its self value implements the Deref trait, and applies it if necessary. Vec<i8> does implement Deref, so we implicitly call its deref method. However, deref takes its self argument by reference, which means that get_m() is now an rvalue appearing in an lvalue context. In this situation, Rust creates a temporary to hold the value, and passes a reference to that. (Keep an eye on this temporary!)
    • We call deref, yielding a slice of type &[i8] borrowing the vector's elements.
    • This slice implements the SliceExt trait, which does have an iter method. Finally! This iter also takes its self argument by reference, and returns a std::slice::Iter holding a reference to the slice.
  • We make the call .peekable(). As before, std::slice::Iter has no peekable method, but it does implement Iterator; IteratorExt is implemented for every Iterator; and IteratorExt does have a peekable method. This takes its self by value, so the Iter is consumed, and we get a std::iter::Peekable back in return, again holding a reference to the slice.
  • This Peekable is then bound to the variable vals, which lives to the end of the function.
  • The temporary holding the original Vec<i8>, to whose elements the Peekable refers, now dies. Oops. This is the borrowed value not living long enough.

But the temporary dies there only because that's the rule for temporaries. If we give it a name, then it lasts as long as its name is in scope:

let vec = get_m();
let mut peekable = vec.iter().peekable();
println!("Saw a {:?}", vals.peek());

I think that's the story. What still confuses me, though, is why that temporary doesn't live longer, even without a name. The Rust reference says, "A temporary's lifetime equals the largest lifetime of any reference that points to it." But that's clearly not the case here.

Jim Blandy
  • 1,536
  • 10
  • 17
  • Thanks very much for taking the time to dig in here! Quick question: where can I go to learn things like "Any method call checks whether its self value implements the Deref trait, and applies it if necessary."? I mean, the language specs are one source; but is there anything higher-level? – Bosh Mar 06 '15 at 22:19
  • 1
    Well, I got it from reading the Rust reference: http://doc.rust-lang.org/reference.html See especially the section on "field expressions". But that's not really very clear; I'm told the reference is due for a major refresh soon. The other source is the docs for the Deref trait itself: http://doc.rust-lang.org/std/ops/trait.Deref.html But that's not great either. I do a lot of experimentation. – Jim Blandy Mar 09 '15 at 18:35
  • I'm not sure what's the best reference for the temporary lifetime rules, but certainly that quote from the reference is incorrect. This blog post covers a lot of the reasoning that went into the current rule, but I don't think it's fully up to date with the rule as implemented: http://smallcultfollowing.com/babysteps/blog/2014/01/09/rvalue-lifetimes-in-rust/ Hopefully, we'll update the reference soon (note also that there is an accepted, but unimplemented, RFC that tweaks the rules in some cases, #66) – Niko Matsakis Mar 12 '15 at 19:04
6

This is happening because you are trying to run your .iter().peekable() on the actual vector inside of get_m(), which is getting re-referenced by vals.

Basically, you want something like this:

fn get_m() -> Vec<i8> {
    vec![1, 2, 3]
}

fn main() {
    let vals = get_m();
    let mut val = vals.iter().peekable();
    println!("Saw a {:?}", val.peek());
}

(Playground)

Result:

Saw a Some(1)
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Ryen Nelsen
  • 367
  • 1
  • 9