Rust代码优化:如何避免循环中clone Vec<i32>以转移所有权?
如何在Rust中避免克隆Vec,直接转移所有权?
问题背景
现有Rust代码功能正常,但循环内的x.v.clone()操作存在性能浪费——因为后续会调用self.a.clear(),原向量不再被使用,理论上可以直接转移self.a[i].v的所有权到新的AStruct中,无需克隆。但移除clone后代码无法编译,需要找到可行的优化方案。
原代码:
use std::collections::HashMap; #[derive(Debug)] struct AStruct { v: Vec<i32>, w: i32, } struct TestStruct { a: Vec<AStruct>, } impl TestStruct { fn test(&mut self) { let mut h: HashMap<Vec<i32>, i32> = HashMap::new(); for x in &self.a { let key = x.v.clone(); let entry = h.entry(key).or_insert(0); *entry += x.w; } self.a.clear(); for (key, value) in h { self.a.push( AStruct { v: key, w: value, } ); } } } fn main() { let a = AStruct { v: vec![1, 2, 3], w: 4, }; let mut t = TestStruct { a: vec![a] }; t.test(); println!("{:?}", t.a); }
解决方案:取出元素所有权而非借用
核心是利用Rust集合的所有权转移方法,直接获取self.a中元素的所有权,避免克隆。推荐两种方式:
方法1:使用Vec::drain()
drain()会返回一个迭代器,获取指定范围内所有元素的所有权,迭代完成后原向量会被自动清空,正好匹配我们后续要调用clear()的场景:
use std::collections::HashMap; #[derive(Debug)] struct AStruct { v: Vec<i32>, w: i32, } struct TestStruct { a: Vec<AStruct>, } impl TestStruct { fn test(&mut self) { let mut h: HashMap<Vec<i32>, i32> = HashMap::new(); // 用drain(..)取出所有元素的所有权,原self.a迭代后自动清空 for x in self.a.drain(..) { // 直接转移x.v的所有权到HashMap的key,无需克隆 let entry = h.entry(x.v).or_insert(0); *entry += x.w; } // 无需再手动调用clear() for (key, value) in h { self.a.push(AStruct { v: key, w: value, }); } } } fn main() { let a = AStruct { v: vec![1, 2, 3], w: 4, }; let mut t = TestStruct { a: vec![a] }; t.test(); println!("{:?}", t.a); }
方法2:使用Vec::take()
take()会将原向量替换为空向量,并返回原向量的所有权,之后可以遍历这个返回的向量处理元素:
use std::collections::HashMap; #[derive(Debug)] struct AStruct { v: Vec<i32>, w: i32, } struct TestStruct { a: Vec<AStruct>, } impl TestStruct { fn test(&mut self) { let mut h: HashMap<Vec<i32>, i32> = HashMap::new(); // 取出原向量的所有权,self.a被替换为空向量 let old_a = self.a.take(); for x in old_a { let entry = h.entry(x.v).or_insert(0); *entry += x.w; } for (key, value) in h { self.a.push(AStruct { v: key, w: value, }); } } } fn main() { let a = AStruct { v: vec![1, 2, 3], w: 4, }; let mut t = TestStruct { a: vec![a] }; t.test(); println!("{:?}", t.a); }
为什么这两种方法可行?
- 两种方式都避免了对
x.v的克隆,直接转移了Vec<i32>的所有权到HashMap中,性能更优。 - 原向量的元素被全部取出后,自身会变为空,完全符合后续要清空的需求,无需额外调用
clear()。
内容的提问来源于stack exchange,提问作者André
相关产品推荐
相关产品推荐

