为什么在这个例子中会出现“借用的值未能活得足够长”的错误提示?

13

编者注:本问题中的代码早于 Rust 1.0 版本。该代码的现代等效版本可以直接编译。

我仍在学习Rust,但很惊讶下面的代码能够编译通过:

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);
}

虽然这样做不会:.iter() (只是把它移到前面)

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);
}

这会导致以下错误:

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();
                          ^~~~~~~~~~~~~~~~~~~

我相信这与Rust的作用域等有关,但我不确定只是将方法调用移到不同行会有什么区别。

2个回答

11

由于没有变量来持有该向量,let range = [1,2,3,4,5,6,7,8,9].iter();语句结束时,该数组将被销毁。这会导致悬垂迭代器指向无处。

C++中也发生了同样的情况,在一个被删除的对象上创建迭代器是可行的。


5
这是正确的:iter()包含对原始切片的引用,在该语句中,它立即被丢弃,因为没有存储在任何地方。编译器会防止使用释放后内存的未定义行为,这几乎肯定会导致错误。 - Chris Morgan
1
请参阅此博客文章,以了解此处的设计空间的详细讨论:http://www.smallcultfollowing.com/babysteps/blog/2014/01/09/rvalue-lifetimes-in-rust/ (从我发布的答案中迁移) - pnkfelix

1
在现代的Rust中,等效的代码可以编译:
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);
}

这是因为字面量(比如数组字面量)现在会自动转换为静态变量,如果需要的话。这里 iter 的结果现在是一个 std::slice::Iter<'static, i32>
另请参见:

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接