为何for循环需获取引用所有权?Rust编译报错与修复解析
原始错误代码
fn main() { let mut v = Vec::new(); foo(&mut v); } fn foo(v_ref: &mut Vec<u8>) { for v in v_ref{ println!("{}", v); } for v in v_ref{ println!("{}", v); } }
编译器错误提示
move occurs because
v_refhas type&mut Vec<u8>, which does not implement theCopytraitv_refmoved due to this implicit call to.into_iter()
编译器给出的修复方案是将第一个for循环修改为:
for v in &mut *v_ref { println!("{}", v); }
问题1:为何需要这样处理?v_ref本身已是引用,for循环结束后不应归还该引用吗?
Rust的for循环本质是调用目标的.into_iter()方法生成迭代器。对于&mut Vec<T>类型来说,它的.into_iter()实现会直接move自身来创建迭代器——因为&mut Vec<T>没有实现Copy trait,调用.into_iter()时没法复制一份引用,只能把原引用转移给迭代器。
第一个for循环执行时,v_ref被move到迭代器中,循环结束后迭代器会被销毁,但被move走的v_ref不会回到原变量里。按照Rust的所有权规则,值一旦被move,原变量就会变成无效状态,所以第二个for循环再用v_ref就会报错。
对比共享引用&Vec<T>:它实现了Copy trait,调用.into_iter()时会复制一份引用,原引用不受影响,所以可以多次迭代。但可变引用为了保证排他性(同一时间只能有一个可变引用指向同一数据),故意不实现Copy,所以只能被move。
问题2:该修复方案为何有效?按我的理解,新的可变引用应该会使旧引用失效。
&mut *v_ref其实是两步操作:
*v_ref解引用原可变引用,得到底层的Vec<u8>(这只是临时操作,不会夺取原引用的所有权);- 给这个临时的
Vec<u8>创建一个新的临时可变引用&mut,类型还是&mut Vec<u8>。
核心在于:这个新生成的可变引用是临时的,生命周期仅限当前for循环。当循环结束,这个临时引用就被销毁了,完全不会影响原有的v_ref——因为我们根本没move原v_ref,只是基于它生成了一个短期的子引用。
for循环对这个临时引用调用.into_iter()时,move的是这个临时引用,不是原v_ref。临时引用销毁后,原v_ref依然有效,所以第二个for循环可以正常使用它。
另外这完全符合Rust的借用规则:两个临时引用是顺序使用的,同一时间只有一个可变引用存在,且它们的生命周期都没超过原v_ref,所以不会触发引用失效的问题。
内容的提问来源于stack exchange,提问作者Andrew Cramer

