2

When I try to compile this code (playground):

fn main() {
    let iter = "abc123".chars().filter(&|&c: &char| c.is_digit(10));
    match iter.clone().take(3).count() {
        3 => println!("{}", iter.collect::<String>()),
        _ => {}
    }
}

I get the following error:

error: borrowed value does not live long enough
 --> test.rs:2:41
  |
2 |     let iter = "abc123".chars().filter(&|c: &char| c.is_digit(10));
  |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value only lives until here
  |                                         |
  |                                         temporary value created here
...
7 | }
  | - temporary value needs to live until here
  |
  = note: consider using a `let` binding to increase its lifetime

I understand that the error is helpfully telling me to declare the closure in the line above with let f = &|c: &char| c.is_digit(10); (working code), but why exactly is this necessary?

I'm also not sure why the closure has to contain two references - &|c: &char|. Doesn't "abc123".chars() simply create an iterator of chars?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
gib
  • 1,951
  • 1
  • 21
  • 24
  • Relevant: http://stackoverflow.com/q/31374051/155423; near duplicates: http://stackoverflow.com/q/28776630/155423, http://stackoverflow.com/q/23969191/155423, [working code](http://play.integer32.com/?gist=feb39ad6c2bd1641dcf463b65d560986&version=stable); TL;DR: you probably want to use `by_ref`. – Shepmaster Nov 12 '16 at 22:22
  • @Shepmaster I guess what I was trying to ask is how to clone an Iterator (before I got confused by the error message). So how would I do [this](https://play.rust-lang.org/?gist=a8f4f33423f100f857ffe2ce4f0263a8&version=stable&backtrace=0) for example (I know this isn't the best way to do it). – gib Nov 12 '16 at 23:19

1 Answers1

5

but why exactly is this necessary?

I don't really know how to explain it much better than the error messages:

temporary value only lives until here
temporary value created here

You are creating a temporary value (the closure itself) in a statement and then taking a reference to it. When the statement ends, the value is destroyed — nothing owns it! The problem is that the code tries to keep the reference to the now-destroyed value. If the compiler allowed this, then when it went to use that reference, who knows what random data would be accessed.

the closure has to contain two references

Well, it doesn't have to. filter(|c| c.is_digit(10)) works just fine; type inference allows c to be automatically typed as &char. The &c only pattern-matches and automatically dereferences the value. That's redundant because method calls automatically dereference.

The bigger issue is that the code is attempting to clone an iterator containing a closure, which you cannot do (1, 2, 3, 4 (goodness, people refuse to search before asking a question)). The clever way that you've chosen to work around this is by cloning a reference to a closure, which is fine.

The problem loops back to taking a reference to something that is destroyed at the end of the statement.


If the goal is to ignore all non-digits, skip over the first 3 digits and then collect the rest of the digits, you can use Iterator::skip:

let iter = "abc123".chars().filter(|c| c.is_digit(10));
let together: String = iter.skip(3).collect();
println!("{}", together);

If the goal is to take only the first 3 digits if and only if there were three digits, then I might always collect those digits, and check to see if that was the end:

let mut iter = "abc123".chars().filter(|c| c.is_digit(10));
let together: String = iter.by_ref().take(3).collect();

if iter.next().is_none() {
    println!("{}", together);
}

This uses Iterator::by_ref. Instead of consuming the iterator, by_ref creates a mutable reference to it. Mutable references to iterators also implement Iterator, so calling take and collect work fine. When those are done, however, iter remains valid.

Community
  • 1
  • 1
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Yeah, sorry, what happened is I originally had something like [this](https://play.rust-lang.org/?gist=080f87f21823940bb52e92bc55a1299c&version=stable&backtrace=0), but then I ended up with the `&|c: &char|` trying to appease the compiler. This is just me trying to get to an MCVE, I know this isn't the best way to do it. This is really enlightening though! – gib Nov 12 '16 at 23:37