老实说,IterMut很狂野。 说起来似乎很荒唐。 当然,它与Iter相同!
从语义上讲,是的。但是共享和可变引用的性质意味着 Iter 是“琐碎的” ,而 IterMut 是合法的向导魔法。
关键洞察力来自于我们对 Iter 的 Iterator 实现:
Copy impl <' a , T > Iterator for Iter <' a , T > {
type Item = & ' a T ;
fn next ( &mut self) -> Option <Self :: Item > { /* stuff */ }
}
可以将其语法糖去除:
Copy impl <' a , T > Iterator for Iter <' a , T > {
type Item = & ' a T ;
fn next <' b >( & ' b mut self) -> Option < & ' a T > { /* stuff */ }
}
next 的签名在输入和输出的生存期之间没有任何约束! 我们为什么在乎? 这意味着我们可以无条件地反复调用 next!
Copy 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 代码开始,然后把所有东西都改成可变的:
Copy 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_mut () . map ( | node | &mut ** node) }
}
}
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_mut () . map ( | node | &mut ** node);
&mut node . elem
})
}
}
Copy > 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_mut () . map ( | node | &mut ** node) }
| ^^^^^^^^^ `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。 只是一个愚蠢的复制粘贴错误。
Copy pub fn iter_mut ( &mut self) -> IterMut <' _ , T > {
IterMut { next : self . head . as_mut () . map ( | node | &mut ** node) }
}
那另一个呢?
哎呀!实际上,我在前面的部分中写 iter 的时候不小心犯了一个错误,而且我们很幸运它能正常工作!
我们刚刚第一次尝试了复制的魔力。当我们引入所有权时,我们说过当你移动东西的时候,你就不能再使用它了。对于某些类型来说,这是完全合理的。我们的好朋友 Box 为我们管理堆上的一个分配,我们当然不希望释放这两段代码的内存。
但是对于其他类型来说,这就是垃圾。整数没有所有权语义,它们只是毫无意义的数字!这就是为什么将整数标记为 Copy 的原因。众所周知,按位复制是完全可复制的。因此,它们有一种超能力: 当移动时,旧值仍然可用。因此,您甚至可以将 Copy 类型从引用中移出,而无需替换!
Rust中的所有数值原语(i32、u64、bool、f32、char等)都是Copy。只要其所有字段都声明为 Copy 类型,也可以将任何用户自定义类型声明为 Copy。
至关重要的是,为什么这段代码起作用了,共享的引用也被复制了! 因为&是副本,所以Option<&> 也是Copy。 因此,当我们执行self.next.map 时,Option 已被复制。 现在,我们无法执行此操作,因为&mut 不是复制(如果您复制了 &mut,则将两个 &mut 移到内存中的同一位置,这是禁止的)。 相反,我们应该适当地使用 Option 来获取它。
Copy fn next ( &mut self) -> Option <Self :: Item > {
self . next . take () . map ( | node | {
self . next = node . next . as_mut () . map ( | node | &mut ** node);
&mut node . elem
})
}
呃...哇 天哪! IterMut正常工作!
让我们来测试一下:
Copy #[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 ));
}
Copy > 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
It works.
天啊。
什么?
我的意思是它实际上应该是有效的,但是通常会有一些愚蠢的东西阻碍它!我们先说清楚:
我们刚刚实现了一段代码,它接受一个单链链表,最多一次返回对列表中每个元素的可变引用。这是静态验证的。而且非常安全。我们不必做任何疯狂的事。
如果你问我的话,那是件大事。这有两个原因可以解释:
我们采用 Option<&mut> ,这样我们就可以独占地访问可变的引用。不用担心有其他人再次使用。
Rust 知道将一个可变的引用切分到指向结构体的子字段中是可以的,因为没有办法“返回” ,而且它们肯定是不相交的。
事实证明,您可以应用这个基本逻辑为数组或树获取一个安全的IterMut!您甚至可以将迭代器设为双端,这样您就可以同时从前面和后面使用迭代器!哇哦!