Rust中调用迭代器next()为何需将迭代器声明为mut可变?
Rust 迭代器可变借用编译问题解析
问题1:调用迭代器的next()方法时,是否真的需要进行可变借用?
是,这是Rust迭代器接口的硬性定义,没有任何例外。
Rust所有迭代器都实现了Iterator trait,该trait中next方法的签名固定为:
fn next(&mut self) -> Option<Self::Item>
方法接收者明确标注为&mut self,也就是说只要调用next(),就必须对迭代器实例持有可变借用,这个要求是写在标准库接口定义里的,不是编译器凭空增加的限制。
问题2:该场景下编译器要求可变借用的底层原因是什么?
迭代器本质是一个记录遍历进度的状态机,调用next()一定会修改迭代器自身的内部状态:
- 最基础的数组、列表迭代器,内部会保存当前遍历到的下标索引,每调用一次
next()就会把下标+1,指向下一个元素,这个修改下标值的动作就是对自身状态的修改 - 示例中用到的
map属于迭代器适配器,它内部会包裹源迭代器,每调用一次next(),首先要调用内部源迭代器的next()取元素(这一步已经在修改源迭代器的状态了),再调用闭包做值转换后返回
Rust的借用规则明确要求:修改一个值的内部状态,必须持有该值的可变引用。如果允许通过不可变引用调用next(),就会出现多个引用同时修改迭代器遍历进度的情况,直接导致重复读、跳读等重入bug,Rust在编译期就把这类问题拦截了。
问题3:此场景中真正需要可变的对象,是迭代器实例i本身,还是迭代器产出的value值?
只有迭代器实例i本身需要可变,迭代器产出的value值完全不需要可变。next()方法修改的只是迭代器自身存储的遍历进度状态,和最终返回给你的value是完全独立的两个实体:你拿到value之后可以声明为不可变直接使用(就像示例代码里写的那样,没有给value加mut也能正常打印),也可以根据自己的需求加mut修改,和迭代器本身的可变性没有任何关系。
当你把let mut i改成let i时,相当于向编译器承诺“i的内部状态绝对不会被修改”,但后续调用next()的动作本身就需要修改i的状态,编译器检测到这个矛盾就会直接报错,提示你给i加上mut标记。
可正常运行的示例代码
fn main() { let x = &vec![1,2,3,5,7,11,13,17,19,23,29,31,37,41,47]; let mut i = x.iter().map(|x|{2*x}); while let Some(value) = i.next() { println!("{}", value); } }
如果去掉i前面的mut,就会触发如下编译错误:
| 3 | let i = x.iter().map(|x|{2*x}); | - help: consider changing this to be mutable: `mut i` 4 | 5 | while let Some(value) = i.next() { | ^^^^^^^^ cannot borrow as mutable
内容的提问来源于stack exchange,提问作者Salathiel Genese
相关产品推荐
相关产品推荐

