13

Editor's note: The code in this question predates Rust 1.0. The equivalent modern version of this code compiles as-is.

I'm still taking baby steps at learning Rust, and was surprised at the following.

I couldn't understand why this code compiles:

use std::iter::AdditiveIterator;

fn main() {

    let range = [1,2,3,4,5,6,7,8,9];
    let sum = range.iter().map(|&x| x * x).filter(|&x| x % 2 == 0).sum();

    println!("{}", sum);
}

While this doesn't: (just moving the .iter() up)

use std::iter::AdditiveIterator;

fn main() {

    let range = [1,2,3,4,5,6,7,8,9].iter();
    let sum = range.map(|&x| x * x).filter(|&x| x % 2 == 0).sum();

    println!("{}", sum);
}

Which renders this error:

test.rs:5:17: 5:36 error: borrowed value does not live long enough
test.rs:5     let range = [1,2,3,4,5,6,7,8,9].iter();
                          ^~~~~~~~~~~~~~~~~~~

I'm sure it has something to do with Rust scopes etc, but I'm not sure I understand how by just moving the method call to a different line makes a difference.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Eran Medan
  • 44,555
  • 61
  • 184
  • 276

2 Answers2

11

The array gets destroyed at the end of the let range = [1,2,3,4,5,6,7,8,9].iter(); statement as there is no variable that holds that vector. This would cause a dangling iterator pointing nowhere.

The same occurs in C++, where it is possible to create iterator on object that gets deleted after.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
c-smile
  • 26,734
  • 7
  • 59
  • 86
  • 5
    This is correct: `iter()` contains a reference to the original slice, and in that statement it is immediately dropped as it is not stored anywhere. The compiler then saves you from the undefined but almost certainly bad behaviour that is use-after-free. – Chris Morgan May 03 '14 at 09:15
  • 1
    See also this blog post for much discussion of the design space here: http://www.smallcultfollowing.com/babysteps/blog/2014/01/09/rvalue-lifetimes-in-rust/ (migrating from an answer I had posted) – pnkfelix May 15 '14 at 12:54
1

In modern Rust, the equivalent code compiles:

fn main() {
    let range = [1, 2, 3, 4, 5, 6, 7, 8, 9].iter();
    let sum: i32 = range.map(|&x| x * x).filter(|&x| x % 2 == 0).sum();
    println!("{}", sum);
}

This is because literals, like the array literal, are now automatically promoted to static variables if they need to be. The result of iter here is now a std::slice::Iter<'static, i32>.

See also:

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