为何Rust中into_iter会移动整个集合?如何规避部分移动问题?
Rust中into_iter导致结构体部分移动的问题
示例场景
use std::collections::HashMap; struct Foo { bar: Vec<i32>, } let foo = Foo { bar: vec![1, 2, 3] }; let bar: HashMap<i32, i32> = foo.bar.into_iter().map(|x|(x,x)).collect();
预期与实际行为
- 预期行为:foo.bar的所有元素移入新HashMap,foo.bar变为空Vec仍可使用,整个foo结构体也能继续使用。
- 实际行为:foo.bar作为整个集合被移动,导致结构体foo发生部分移动,无法再被使用。
问题解析
Rust中into_iter()的设计目标是消耗整个容器的所有权,而非仅迭代元素并保留空容器。对于Vec来说,调用into_iter()会直接获取该Vec的所有权,迭代完成后原Vec会被销毁。
当执行foo.bar.into_iter()时,foo.bar的所有权被转移给了迭代器,此时结构体foo失去了bar字段的所有权,进入部分移动状态。Rust的安全规则禁止使用不完整的结构体,因此整个foo都无法再被访问。
解决方案
方案1:使用drain(..)迭代并保留空容器
drain(..)会移除容器内的所有元素,返回一个消耗元素的迭代器,同时原容器会被保留为空(所有权仍属于原结构体)。这是最符合预期场景的方案:
use std::collections::HashMap; struct Foo { bar: Vec<i32>, } let mut foo = Foo { bar: vec![1, 2, 3] }; let bar: HashMap<i32, i32> = foo.bar.drain(..).map(|x|(x,x)).collect(); // foo.bar已为空,foo可正常使用 println!("{:?}", foo.bar); // 输出 []
方案2:克隆容器后使用into_iter()
如果不需要清空原容器,只是想基于元素创建HashMap,可以先克隆Vec,再对克隆体使用into_iter()。缺点是会产生额外的内存开销,适合小容量集合:
use std::collections::HashMap; struct Foo { bar: Vec<i32>, } let foo = Foo { bar: vec![1, 2, 3] }; let bar: HashMap<i32, i32> = foo.bar.clone().into_iter().map(|x|(x,x)).collect(); // foo和foo.bar均可正常使用
方案3:用std::mem::take()手动替换为空容器
std::mem::take()会取出原容器的所有权,同时将原字段替换为该类型的默认值(对于Vec来说就是空Vec),之后可以对取出的容器使用into_iter():
use std::collections::HashMap; use std::mem; struct Foo { bar: Vec<i32>, } let mut foo = Foo { bar: vec![1, 2, 3] }; let temp_vec = mem::take(&mut foo.bar); let bar: HashMap<i32, i32> = temp_vec.into_iter().map(|x|(x,x)).collect(); // foo.bar已为空,foo可正常使用
内容的提问来源于stack exchange,提问作者Kurai Akoraito
相关产品推荐
相关产品推荐

