2.6 IterMut

原文链接: https://rust-unofficial.github.io/too-many-lists/second-iter-mut.html

说实话,IterMut 很狂野。这话本身听起来就很狂野;从语义上看,它和 Iter 一模一样吧!

语义上是的,但共享引用与可变引用的性质意味着 Iter 是「轻而易举」的,而 IterMut 则是正经的巫师魔法。

关键洞察来自我们对 Iter 的 Iterator 实现:

1
2
3
4
5
impl<'a, T> Iterator for Iter<'a, T> {
    type Item = &'a T;

    fn next(&mut self) -> Option<Self::Item> { /* stuff */ }
}

可以脱糖为:

1
2
3
4
5
impl<'a, T> Iterator for Iter<'a, T> {
    type Item = &'a T;

    fn next<'b>(&'b mut self) -> Option<&'a T> { /* stuff */ }
}

next 的签名在输入与输出的生命周期之间建立了零约束!为什么在意?意味着可以无条件地反复调用 next!

1
2
3
4
5
6
7
let mut list = List::new();
list.push(1); list.push(2); list.push(3);

let mut iter = list.iter();
let x = iter.next().unwrap();
let y = iter.next().unwrap();
let z = iter.next().unwrap();

酷!

对共享引用这绝对没问题,因为要点就是可以同时有很多个。可变引用则不能共存,要点就是独占。

结果是,用安全代码写 IterMut 明显更难(我们还没讲那意味着什么……)。令人惊讶的是,对很多结构 IterMut 其实可以完全安全地实现!

先把 Iter 的代码拿过来,全部改成可变:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
pub struct IterMut<'a, T> {
    next: Option<&'a mut Node<T>>,
}

impl<T> List<T> {
    pub fn iter_mut(&self) -> IterMut<'_, T> {
        IterMut { next: self.head.as_deref_mut() }
    }
}

impl<'a, T> Iterator for IterMut<'a, T> {
    type Item = &'a mut T;

    fn next(&mut self) -> Option<Self::Item> {
        self.next.map(|node| {
            self.next = node.next.as_deref_mut();
            &mut node.elem
        })
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
> cargo build
error[E0596]: cannot borrow `self.head` as mutable, as it is behind a `&` reference
  --> src/second.rs:95:25
   |
94 |     pub fn iter_mut(&self) -> IterMut<'_, T> {
   |                     ----- help: consider changing this to be a mutable reference: `&mut self`
95 |         IterMut { next: self.head.as_deref_mut() }
   |                         ^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable

error[E0507]: cannot move out of borrowed content
   --> src/second.rs:103:9
    |
103 |         self.next.map(|node| {
    |         ^^^^^^^^^ cannot move out of borrowed content

两个不同错误。第一个很清楚,还告诉我们怎么修:不能把共享引用升级为可变引用,所以 iter_mut 需要 &mut self。只是愚蠢的复制粘贴错误。

1
2
3
pub fn iter_mut(&mut self) -> IterMut<'_, T> {
    IterMut { next: self.head.as_deref_mut() }
}

另一个呢?

糟糕!写上一节 iter 实现时我其实犯了个错,只是碰巧能跑!

我们第一次撞上 Copy 的魔法。讲所有权时说过,移动之后就不能再用了。对某些类型这完全合理。老朋友 Box 帮我们管理堆上的分配,我们当然不希望两段代码都觉得自己该释放那块内存。

但对其他类型这纯属垃圾。整数没有所有权语义,只是无意义的数字!所以整数标为 Copy。Copy 类型可以通过按位复制完美复制。因此它们有超能力:移动之后旧值仍然可用。于是甚至可以从引用里移出 Copy 类型而无需替换!

Rust 里所有数值原语(i32、u64、bool、f32、char 等)都是 Copy。只要所有组成部分都是 Copy,用户自定义类型也可以声明为 Copy。

关键的是这段代码能跑的原因:共享引用也是 Copy!因为 & 是 Copy,Option<&> 也是 Copy。所以 self.next.map 没问题,Option 只是被复制了。现在不行了,因为 &mut 不是 Copy(复制 &mut 会有两个 &mut 指向同一内存,禁止)。应该正确地 take 这个 Option。

1
2
3
4
5
6
fn next(&mut self) -> Option<Self::Item> {
    self.next.take().map(|node| {
        self.next = node.next.as_deref_mut();
        &mut node.elem
    })
}
1
> cargo build

呃……哇。IterMut 直接就能用了!

测试一下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#[test]
fn iter_mut() {
    let mut list = List::new();
    list.push(1); list.push(2); list.push(3);

    let mut iter = list.iter_mut();
    assert_eq!(iter.next(), Some(&mut 3));
    assert_eq!(iter.next(), Some(&mut 2));
    assert_eq!(iter.next(), Some(&mut 1));
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
> cargo test

     Running target/debug/lists-5c71138492ad4b4a

running 6 tests
test first::test::basics ... ok
test second::test::basics ... ok
test second::test::iter_mut ... ok
test second::test::into_iter ... ok
test second::test::iter ... ok
test second::test::peek ... ok

test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured

没错。能用。

天哪。

什么。

好吧,它本来就该能工作,但通常总有什么愚蠢的东西挡路!说清楚:

我们刚实现了一段代码,接收单链表,最多一次返回链表中每个元素的可变引用。而且静态验证保证如此。完全安全。我们也没做什么疯狂的事。

要我说,这挺了不起的。能工作有几个原因:

  • 我们对 Option<&mut> 做 take,独占可变引用,不用担心有人再看一眼。
  • Rust 理解可以把可变引用拆成所指结构体的子字段,因为没法「往上走回去」,而且它们肯定不相交。

同样的基本逻辑也能为数组或树得到安全的 IterMut!甚至可以做 DoubleEnded 迭代器,从前和后同时消费!哇!

最后修改 August 23, 2026: 更新 (499855b16)