为什么iter()将列表借用为不可变的?

kin*_*luo 0 rust

使用完全过多的链接列表学习Rust时,iter()创建一个迭代器:

impl<T> List<T> {
    pub fn iter(&self) -> Iter<T> {
        Iter { next: self.head.as_ref().map(|node| &**node) }
    }
}
Run Code Online (Sandbox Code Playgroud)

我尝试测试迭代器是否阻止对列表进行进一步修改:

let mut list = List::new();
list.push(1);

let mut iter = list.iter();
list.pop();
Run Code Online (Sandbox Code Playgroud)

编译器报告错误:

list2.rs:114:1: 114:5 error: cannot borrow `list` as mutable because it is also borrowed as immutable [E0502]
list2.rs:114 list.pop();
             ^~~~
list2.rs:113:24: 113:28 note: previous borrow of `list` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `list` until the borrow ends
list2.rs:113         let mut iter = list.iter();
                                    ^~~~
Run Code Online (Sandbox Code Playgroud)

看来Rust确实会阻止不安全的操作,但从语法上讲,为什么要list.iter()借用list?在该方法中,它只返回head元素的引用.

Chr*_*son 5

来自链接的文章:

pub struct Iter<T> {
    next: Option<&Node<T>>,
}
Run Code Online (Sandbox Code Playgroud)

所以迭代器有一个参考.扩展以使生命周期更加明确:

pub struct Iter<'a, T> {
    next: Option<&'a Node<T>>,
}
Run Code Online (Sandbox Code Playgroud)

现在看一下iter方法:

impl<T> List<T> {
    pub fn iter(&self) -> Iter<T> {
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

由于Rust的借用规则也在宣言中停止,因此我将实施清楚地省略了.现在,如果我们将删除的类型放回去:

pub fn iter<'a>(&'a self) -> Iter<'a, T> {...}
Run Code Online (Sandbox Code Playgroud)

由于只有一个&self参数,返回值获得相同的elided生命周期.请参阅elided lifetimes文档.

最终结果是它与Iter<'a, T>传递给iter()方法的引用具有相同的生命周期- 因此引用的生命周期必须至少与Iter对象一样长.因此pop,在Iter活着的时候你不能可变地(对于方法)借用.

这是有充分理由的; 如果pop要取得成功,那么迭代器就会有一个悬空引用前头,导致记忆不安全.