iter vs into_iter:Rust 迭代领域的巅峰对决
\n\n如果你曾经在 Rust 中编写过遍历向量的循环,你很可能遇到过三个“孪生兄弟”:iter()、into_iter() 和 iter_mut()。乍一看,它们做的事情都一样——让你遍历元素。但它们之间的差异比表面看起来要深刻得多,并且根植于 Rust 的核心——所有权系统。
想象一下,你是一名咖啡师,而你的集合就是咖啡豆。iter() 让你观察豆子,闻一闻,但不能触碰。into_iter() 就像你拿起豆子,研磨它们,制作浓缩咖啡,之后豆子就消失了。而 iter_mut() 则是你不仅可以观察,还可以直接在袋子里研磨豆子,改变它们的形态。
今天,我们将像 Stack Overflow 上解释的那样,深入剖析 iter vs into_iter,但会结合生动的例子并深入底层原理。我们开始吧!
\n\nRust 中的迭代器是什么?
\n\n迭代器是一个知道如何从集合中顺序获取元素的对象。在 Rust 中,迭代器是惰性的:在你调用 next() 之前,它们什么也不做。但关键在于,每种迭代方法对数据所有权的处理方式不同。
以下是三种主要方式:
\n- \n
iter()— 借用元素(不获取所有权) \n into_iter()— 消费集合(获取所有权) \n iter_mut()— 可变借用 \n
让我们看看代码。假设我们有一个名字的向量:
\n\nlet names = vec!["安娜".to_string(), "鲍里斯".to_string(), "维克多".to_string()];\n\n示例 1:iter() — 只看不碰
\n\nfn main() {\n let names = vec!["安娜".to_string(), "鲍里斯".to_string(), "维克多".to_string()];\n \n // iter() 返回 &String(引用)\n for name in names.iter() {\n println!("你好,{}!", name);\n }\n \n // names 仍然可用!\n println!("向量仍然存在:{:?}", names);\n}\n\n注意:循环之后,我们可以放心地使用 names。为什么?因为 iter() 返回了每个元素的引用。所有权仍然属于向量。这是安全且可预测的。
示例 2:into_iter() — 破釜沉舟
\n\nfn main() {\n let names = vec!["安娜".to_string(), "鲍里斯".to_string(), "维克多".to_string()];\n \n // into_iter() 返回 String(所有权被转移)\n for name in names.into_iter() {\n println!("再见,{}!", name);\n }\n \n // 错误!names 不再存在\n // println!("向量仍然存在:{:?}", names); // ❌ 借用了已移动的值\n}\n\n这就是关键区别。into_iter() 消费了集合。每个元素都被移入循环,原始向量变得无效。如果你需要保留集合以供后续使用——into_iter() 不是你的选择。
示例 3:iter_mut() — 改变而不破坏
\n\nfn main() {\n let mut names = vec!["安娜".to_string(), "鲍里斯".to_string(), "维克多".to_string()];\n \n // iter_mut() 返回 &mut String(可变引用)\n for name in names.iter_mut() {\n name.push_str(" (客人)");\n }\n \n println!("修改后的向量:{:?}", names);\n // 输出:["安娜 (客人)", "鲍里斯 (客人)", "维克多 (客人)"]\n}\n\n这里我们原地修改元素,但不获取所有权。非常适合进行转换操作。
\n\n底层原理:每个方法返回什么?
\n\n要真正理解 iter vs into_iter,让我们看看标准库。对于向量 Vec<T>:
- \n
iter()返回std::slice::Iter<T>— 实现了Iterator<Item = &T>