0

I have an array defined in the form of a constant, outside any function:

const VALUES: [(char, &str); 2] = [('A', "abc"), ('B', "acb")];

I'm attempting to use the find() method located on the Iterator, in order to extract a single element from an array based on a predicate:

VALUES.iter().find(|&&(name, _)| name == 'A');

In this form, it works fine. I am, however, unable to evaluate the found element into anything, as as soon as I attempt creating a let binding, trying to bind the result, which, according to the docs should come back as an Option<T>.

Let's change the second line to that which doesn't work:

const VALUES: [(char, &str); 2] = [('A', "abc"), ('B', "acb")];

fn main() {
    let result = VALUES.iter().find(|&&(name, _)| name == 'A');
}

(Playground)

One would expect this to return the Option<T> as according to the docs, but instead I get back a compilation error:

error: borrowed value does not live long enough
 --> src/main.rs:4:63
  |
4 |     let result = VALUES.iter().find(|&&(name, _)| name == 'A');
  |                  ------ temporary value created here          ^ temporary value dropped here while still borrowed
5 | }
  | - temporary value needs to live until here
  |
  = note: consider using a `let` binding to increase its lifetime

I'm utterly confused; I'm certain I've just messed up with the "borrow checker". Perhaps someone can point me in the right direction?

kmdreko
  • 42,554
  • 6
  • 57
  • 106
D. Ataro
  • 1,711
  • 17
  • 38
  • I asked a concise Question for common rust error `error[E0716]` [error E0716: temporary value dropped while borrowed (rust)](https://stackoverflow.com/questions/71626083/error-e0716-temporary-value-dropped-while-borrowed-rust). It links back to this Question. – JamesThomasMoon Mar 26 '22 at 07:24

2 Answers2

2

The problem is in the specific way you're turning that array into an iterator.

First of all, consts in Rust do not actually exist anywhere. Instead, they're substituted by-value everywhere they're used. So every time you use a constant, you're getting a new copy of it.

Secondly, you're using IntoIterator::into_iter. This takes the subject by value and converts it into an iterator.

These combine with the third part: IntoIterator is not implemented for fixed-size arrays. It's only implemented for pointers to fixed-size arrays. So, in order to invoke into_iter, the compiler has to insert an automatic borrow of the invocant.

So, what's actually happening is this:

let t = {
    // Make a new copy of `VALUES`.
    let values: [(char, &str); 5] = VALUES;
    // Borrow it.
    let values: &[_] = &values;
    // Call `into_iter` on the borrow.
    IntoIterator::into_iter(values).find(|&&(name, _)| name == 'A')
};

This causes problems because the compiler has to simultaneously copy and borrow VALUES in order to get an iterator. Like all temporaries, said copy only lives as long as the statement, but the borrow (by being bound to a variable) has to live longer than that.

The best solution is to make VALUES a pointer to the array. This prevents copying the entire array; instead, you only copy the pointer on each use.

const VALUES: &[(char, &str)] = &[...];

Alternately, you could explicitly make a copy of VALUES and store it in a variable, then use into_iter on that. However, like before, this will introduce unnecessary copying.

DK.
  • 55,277
  • 5
  • 189
  • 162
  • Const arrays actually exist in memory. [This code](https://play.rust-lang.org/?gist=023a4c4ea0cc8cac7a890bbd3a05d876&version=stable&backtrace=0) works. The temporary value that gets dropped isn't array, it's array slice. – red75prime Jun 18 '17 at 11:32
  • 1
    @red75prime: My understanding is that *that* behaviour is a special case where temporaries that are immediately borrowed are promoted, as that pattern is relatively common and would otherwise be more painful. – DK. Jun 18 '17 at 11:49
  • Another option is to use `static`, which actially does exist in memory and therefore can be borrowed normally. – zstewart Jun 18 '17 at 14:25
1

DK.'s answer is correct, but I'd suggest a simpler change to get your code to work — use static instead of const.

From the documentation about static and const:

More specifically, constants in Rust have no fixed address in memory.

static items aren’t inlined upon use. This means that there is only one instance for each value, and it’s at a fixed location in memory.

Switching to static allows you to get a memory address of the thing you are iterating over.

static VALUES: [(char, &str); 2] = [('A', "abc"), ('B', "acb")];

fn main() {
    let result = VALUES.iter().find(|&&(name, _)| name == 'A');
}

Here, result is an Option<&(char, &str)>.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366