diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 3532df60..9b54831d 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -162,8 +162,8 @@ - [还可以的单向链表](too-many-lists/ok-stack/intro.md) - [优化类型定义](too-many-lists/ok-stack/type-optimizing.md) - [定义 Peek 函数](too-many-lists/ok-stack/peek.md) - - [IntoIter 和 Iter](too-many-lists/iter.md) - + - [IntoIter 和 Iter](too-many-lists/ok-stack/iter.md) + - [IterMut以及完整代码](too-many-lists/ok-stack/itermut.md) - [易混淆概念解析](confonding/intro.md) - [切片和切片引用](confonding/slice.md) - [Eq 和 PartialEq](confonding/eq.md) diff --git a/src/basic/ownership/ownership.md b/src/basic/ownership/ownership.md index ef3ef21f..fe1e26fb 100644 --- a/src/basic/ownership/ownership.md +++ b/src/basic/ownership/ownership.md @@ -262,7 +262,7 @@ Rust 有一个叫做 `Copy` 的特征,可以用在类似整型这样在栈中 - 所有浮点数类型,比如 `f64`。 - 字符类型,`char`。 - 元组,当且仅当其包含的类型也都是 `Copy` 的时候。比如,`(i32, i32)` 是 `Copy` 的,但 `(i32, String)` 就不是。 -- 引用类型,例如[转移所有权](#转移所有权)中的最后一个例子 +- 不可变引用 `&T` ,例如[转移所有权](#转移所有权)中的最后一个例子,**但是注意: 可变引用 `&mut T` 是不可以 Copy的** ## 函数传值与返回 diff --git a/src/too-many-lists/iter.md b/src/too-many-lists/ok-stack/iter.md similarity index 100% rename from src/too-many-lists/iter.md rename to src/too-many-lists/ok-stack/iter.md diff --git a/src/too-many-lists/ok-stack/itermut.md b/src/too-many-lists/ok-stack/itermut.md new file mode 100644 index 00000000..b1dc4706 --- /dev/null +++ b/src/too-many-lists/ok-stack/itermut.md @@ -0,0 +1,326 @@ +# IterMut以及完整代码 +上一章节中我们讲到了要为 `List` 实现三种类型的迭代器并实现了其中两种: `IntoIter` 和 `Iter`。下面再来看看最后一种 `IterMut`。 + +再来回顾下 `Iter` 的实现: +```rust +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { /* stuff */ } +} +``` + +这段代码可以进行下脱糖( desugar ): +```rust +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + fn next<'b>(&'b mut self) -> Option<&'a T> { /* stuff */ } +} +``` + +可以看出 `next` 方法的输入和输出之间的生命周期并没有关联,这样我们就可以无条件的一遍又一遍地调用 `next`: +```rust +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`。 + +先将之前的代码修改成可变的: +```rust +pub struct IterMut<'a, T> { + next: Option<&'a mut Node>, +} + +impl List { + 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.next.map(|node| { + self.next = node.next.as_deref_mut(); + &mut node.elem + }) + } +} +``` + +```shell +> 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 +``` + +果不其然,两个错误发生了。第一错误看上去很清晰,甚至告诉了我们该如何解决: +```rust +pub fn iter_mut(&mut self) -> IterMut<'_, T> { + IterMut { next: self.head.as_deref_mut() } +} +``` + +但是另一个好像就没那么容易了。但是之前的代码就可以工作啊,为何这里就不行了? + +原因在于有些类型可以 [Copy](https://course.rs/basic/ownership/ownership.html#拷贝浅拷贝),有些不行。而`Option` 和不可变引用 `&T` 恰恰是可以 Copy 的,但尴尬的是,可变引用 `&mut T` 不可以,因此这里报错了。 + +因此我们需要使用 `take` 方法来处理这种情况: +```rust +fn next(&mut self) -> Option { + self.next.take().map(|node| { + self.next = node.next.as_deref_mut(); + &mut node.elem + }) +} +``` + +```shell +> cargo build +``` + +老规矩,来测试下: +```rust +#[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)); +} +``` + +```shell +> 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 +``` + +最终,我们完成了迭代器的功能,下面是完整的代码。 + +## 完整代码 + +```rust +pub struct List { + head: Link, +} + +type Link = Option>>; + +struct Node { + elem: T, + next: Link, +} + +impl List { + pub fn new() -> Self { + List { head: None } + } + + pub fn push(&mut self, elem: T) { + let new_node = Box::new(Node { + elem: elem, + next: self.head.take(), + }); + + self.head = Some(new_node); + } + + pub fn pop(&mut self) -> Option { + self.head.take().map(|node| { + self.head = node.next; + node.elem + }) + } + + pub fn peek(&self) -> Option<&T> { + self.head.as_ref().map(|node| { + &node.elem + }) + } + + pub fn peek_mut(&mut self) -> Option<&mut T> { + self.head.as_mut().map(|node| { + &mut node.elem + }) + } + + pub fn into_iter(self) -> IntoIter { + IntoIter(self) + } + + pub fn iter(&self) -> Iter<'_, T> { + Iter { next: self.head.as_deref() } + } + + pub fn iter_mut(&mut self) -> IterMut<'_, T> { + IterMut { next: self.head.as_deref_mut() } + } +} + +impl Drop for List { + fn drop(&mut self) { + let mut cur_link = self.head.take(); + while let Some(mut boxed_node) = cur_link { + cur_link = boxed_node.next.take(); + } + } +} + +pub struct IntoIter(List); + +impl Iterator for IntoIter { + type Item = T; + fn next(&mut self) -> Option { + // access fields of a tuple struct numerically + self.0.pop() + } +} + +pub struct Iter<'a, T> { + next: Option<&'a Node>, +} + +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + fn next(&mut self) -> Option { + self.next.map(|node| { + self.next = node.next.as_deref(); + &node.elem + }) + } +} + +pub struct IterMut<'a, T> { + next: Option<&'a mut Node>, +} + +impl<'a, T> Iterator for IterMut<'a, T> { + type Item = &'a mut T; + + fn next(&mut self) -> Option { + self.next.take().map(|node| { + self.next = node.next.as_deref_mut(); + &mut node.elem + }) + } +} + +#[cfg(test)] +mod test { + use super::List; + + #[test] + fn basics() { + let mut list = List::new(); + + // Check empty list behaves right + assert_eq!(list.pop(), None); + + // Populate list + list.push(1); + list.push(2); + list.push(3); + + // Check normal removal + assert_eq!(list.pop(), Some(3)); + assert_eq!(list.pop(), Some(2)); + + // Push some more just to make sure nothing's corrupted + list.push(4); + list.push(5); + + // Check normal removal + assert_eq!(list.pop(), Some(5)); + assert_eq!(list.pop(), Some(4)); + + // Check exhaustion + assert_eq!(list.pop(), Some(1)); + assert_eq!(list.pop(), None); + } + + #[test] + fn peek() { + let mut list = List::new(); + assert_eq!(list.peek(), None); + assert_eq!(list.peek_mut(), None); + list.push(1); list.push(2); list.push(3); + + assert_eq!(list.peek(), Some(&3)); + assert_eq!(list.peek_mut(), Some(&mut 3)); + + list.peek_mut().map(|value| { + *value = 42 + }); + + assert_eq!(list.peek(), Some(&42)); + assert_eq!(list.pop(), Some(42)); + } + + #[test] + fn into_iter() { + let mut list = List::new(); + list.push(1); list.push(2); list.push(3); + + let mut iter = list.into_iter(); + assert_eq!(iter.next(), Some(3)); + assert_eq!(iter.next(), Some(2)); + assert_eq!(iter.next(), Some(1)); + assert_eq!(iter.next(), None); + } + + #[test] + fn iter() { + let mut list = List::new(); + list.push(1); list.push(2); list.push(3); + + let mut iter = list.iter(); + assert_eq!(iter.next(), Some(&3)); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.next(), Some(&1)); + } + + #[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)); + } +} +``` \ No newline at end of file diff --git a/内容变更记录.md b/内容变更记录.md index 24de72ca..25391e17 100644 --- a/内容变更记录.md +++ b/内容变更记录.md @@ -3,6 +3,7 @@ ## 2022-03-13 +- 新增章节: [还 OK 的单向链表 - IterMut](https://course.rs/too-many-lists/ok-stack/itermut.html) - 新增章节: [还 OK 的单向链表 - IntoIter 和 Iter](https://course.rs/too-many-lists/iter.html) - 优化[进一步深入特赠 - 关联类型](https://course.rs/basic/trait/advance-trait.html#关联类型)中的部分内容,感谢 AllenYu0018 的[提示](https://github.com/sunface/rust-course/discussions/392).