如何在Rust结构体中传递闭包实现类似方法重写、编译期确定逻辑的功能
问题分析
你之前的写法编译失败主要有三个核心原因:
- 泛型参数
F由调用侧决定,但你在new方法中根据运行时bool参数返回两种不同类型的闭包,Rust中每个闭包都是独有的编译期类型,二者类型不兼容,无法统一为同一个F - 闭包参数类型不匹配:
Vec<i32>调用iter()得到的元素是&&i32类型,你写的闭包接收i32类型,类型不匹配 - 你的核心需求是编译期确定逻辑,用运行时
bool参数本身就和需求冲突,应该把排序策略提到编译期泛型参数的层面
推荐解决方案:编译期策略模式(零开销)
用零大小类型作为排序策略的泛型参数,完全在编译期确定比较逻辑,没有任何运行时分支和额外内存开销,是Rust中实现这类需求的标准写法:
trait SortStrategy { /// 返回是否应该保留a作为最优值 fn should_keep(a: &i32, b: &i32) -> bool; } // 升序策略:取最大值 struct Ascending; impl SortStrategy for Ascending { #[inline(always)] // 强制内联,完全消除函数调用开销 fn should_keep(a: &i32, b: &i32) -> bool { a >= b } } // 降序策略:取最小值 struct Descending; impl SortStrategy for Descending { #[inline(always)] fn should_keep(a: &i32, b: &i32) -> bool { a <= b } } // 泛型参数S是排序策略,零大小类型不占内存 struct MyList<S> { ladder: Vec<i32>, _strategy: std::marker::PhantomData<S>, // 标记使用了S泛型,无运行时开销 } impl<S: SortStrategy> MyList<S> { pub fn new(data: &[i32]) -> Self { Self { ladder: data.to_vec(), _strategy: std::marker::PhantomData, } } pub fn get_best(&self) -> Option<&i32> { self.ladder.iter().reduce(|a, b| { if S::should_keep(a, b) { a } else { b } }) } } fn main() { // 编译期指定升序策略 let x = MyList::<Ascending>::new(&[10, 4, 30, 2, 5, 2]); let r = x.get_best(); println!("{:?}", r); // 输出Some(30) // 编译期指定降序策略 let x = MyList::<Descending>::new(&[10, 4, 30, 2, 5, 2]); let r = x.get_best(); println!("{:?}", r); // 输出Some(2) }
如果需要运行时动态切换策略
如果你确实有运行时切换排序逻辑的需求,可以把闭包装箱为特征对象统一类型,这种方式会有极少量的动态分发开销,和你原来的API完全兼容:
struct MyList { ladder: Vec<i32>, compare: Box<dyn Fn(&i32, &i32) -> bool>, } impl MyList { pub fn new(data: &[i32], sort_ascending: bool) -> Self { let compare = if sort_ascending { Box::new(|a: &i32, b: &i32| a >= b) } else { Box::new(|a: &i32, b: &i32| a <= b) }; Self { ladder: data.to_vec(), compare, } } pub fn get_best(&self) -> Option<&i32> { self.ladder.iter().reduce(|a, b| { if (self.compare)(a, b) { a } else { b } }) } } fn main() { let x = MyList::new(&[10, 4, 30, 2, 5, 2], true); let r = x.get_best(); println!("{:?}", r); let x = MyList::new(&[10, 4, 30, 2, 5, 2], false); let r = x.get_best(); println!("{:?}", r); }
内容的提问来源于stack exchange,提问作者Ian Ash
相关产品推荐
相关产品推荐

