为何Rust中函数调用不编译,但手动内联版本可正常运行?
问题分析与解决方案
为什么fold1函数与内联版本行为不同?
你的fold1函数泛型约束要求闭包返回迭代器的元素类型(I::Item)。当传入v.iter()时,迭代器元素是&X,因此闭包必须返回&X,但你的闭包返回的是X,直接触发类型不匹配错误。
你提到的“内联版本可编译”,实际是因为你隐式改变了初始值的类型——如果代码真能通过编译,你肯定是把初始值从&X转换成了X(比如显式解引用后克隆:(*it.next().unwrap()).clone(),或使用cloned()方法)。此时fold的初始值是X,闭包接收X和&X,返回X,编译器会自动处理&X的解引用(访问b.x时自动解引用),因此编译通过。但你写的内联代码里的clone()调用实际上会返回&X,这时候应该报和fold1一样的错误,大概率是笔误导致的误解。
实现仅克隆第一个元素的fold1函数
我们可以利用标准库的ToOwned trait,实现同时支持值迭代器和引用迭代器的fold1,满足“仅克隆第一个元素”的需求:
use std::borrow::ToOwned; #[derive(Clone)] struct X { x: i32, } fn fold1<I, F>(mut it: I, f: F) -> <I::Item as ToOwned>::Owned where I: Iterator, I::Item: ToOwned, F: FnMut(<I::Item as ToOwned>::Owned, I::Item) -> <I::Item as ToOwned>::Owned, { let first = it.next().expect("Iterator must not be empty"); let init = first.to_owned(); it.fold(init, f) } fn main() { let v = vec![X { x: 1 }, X { x: 2 }]; // 处理引用迭代器:仅克隆第一个元素,后续元素以引用传入 let s = fold1(v.iter(), |a, b| X { x: a.x + b.x }); println!("{}", s.x); // 输出3 // 处理值迭代器:克隆第一个元素,后续元素以值传入 let s2 = fold1(v.into_iter(), |a, b| X { x: a.x + b.x }); println!("{}", s2.x); // 输出3 }
代码说明
ToOwned是标准库提供的trait,用于从引用类型生成所有权类型,同时对值类型默认实现为返回自身fold1的返回类型是迭代器元素的所有权类型:对于&X返回X,对于X返回X- 仅对第一个元素调用
to_owned()完成克隆,后续元素保持原类型(引用或值)传入闭包
内容的提问来源于stack exchange,提问作者Nikola Benes
相关产品推荐
相关产品推荐

