如何根据配置使用不同闭包对Rust Vector进行排序?
解决Rust中条件选择排序key函数的编译问题
我来帮你搞定这两个编译难题,核心原因都是Rust静态类型系统要求变量/闭包返回值必须是单一确定的类型,而你尝试的两种场景都出现了类型不统一的情况,下面分别给出清晰的解决方案:
问题1:根据条件选择不同排序key(name/age)
你的第一段代码编译失败,是因为两个闭包属于不同的匿名类型,而且返回的&u32和&String也是完全不同的类型,无法赋值给同一个key_func变量。
方案1:用枚举统一返回类型
我们可以定义一个枚举来封装两种可能的key类型,并为它实现Ord trait(因为sort_by_key要求key类型必须实现Ord):
struct Person { name: String, age: u32, } // 枚举封装两种排序key类型 enum PersonSortKey<'a> { Age(&'a u32), Name(&'a String), } // 实现Ord trait,处理不同key的比较逻辑 impl<'a> Ord for PersonSortKey<'a> { fn cmp(&self, other: &Self) -> std::cmp::Ordering { match (self, other) { (PersonSortKey::Age(a), PersonSortKey::Age(b)) => a.cmp(b), (PersonSortKey::Name(a), PersonSortKey::Name(b)) => a.cmp(b), // 理论上不会走到这里,因为我们只会选择同一种key类型排序 _ => panic!("Cannot compare different key types"), } } } // 必须配套实现的trait impl<'a> PartialOrd for PersonSortKey<'a> { fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { Some(self.cmp(other)) } } impl<'a> PartialEq for PersonSortKey<'a> { fn eq(&self, other: &Self) -> bool { match (self, other) { (PersonSortKey::Age(a), PersonSortKey::Age(b)) => a == b, (PersonSortKey::Name(a), PersonSortKey::Name(b)) => a == b, _ => false, } } } impl<'a> Eq for PersonSortKey<'a> {} fn main() { let sort_by = "age"; let mut x = vec![ Person { name: "Peter".to_string(), age: 18, }, Person { name: "Frank".to_string(), age: 55, }, ]; // 现在两个分支返回的都是PersonSortKey类型,类型统一 let key_func: fn(&Person) -> PersonSortKey = if sort_by == "age" { |item| PersonSortKey::Age(&item.age) } else if sort_by == "name" { |item| PersonSortKey::Name(&item.name) } else { // 默认按年龄排序 |item| PersonSortKey::Age(&item.age) }; x.sort_by_key(key_func); // 验证排序结果 for person in x { println!("{}: {}", person.name, person.age); } }
方案2:直接改用sort_by(更简洁)
如果不想写枚举的trait实现,直接用sort_by代替sort_by_key,在闭包里直接返回Ordering,完全避开类型不统一的问题:
struct Person { name: String, age: u32, } fn main() { let sort_by = "age"; let mut x = vec![ Person { name: "Peter".to_string(), age: 18, }, Person { name: "Frank".to_string(), age: 55, }, ]; x.sort_by(|a, b| { if sort_by == "age" { a.age.cmp(&b.age) } else if sort_by == "name" { a.name.cmp(&b.name) } else { a.age.cmp(&b.age) } }); }
问题2:结合Reverse实现条件反向排序
第二段代码编译失败,是因为闭包返回的Reverse<&u32>和&u32是不同类型,Rust无法推断出统一的返回类型。
最优方案:改用sort_by处理反向逻辑
这是最简洁的解决方式,直接在sort_by的闭包里处理正向/反向排序,不需要额外的类型封装:
use std::cmp::Reverse; struct Person { name: String, age: u32, } fn main() { let reverse = true; let mut x = vec![ Person { name: "Peter".to_string(), age: 18, }, Person { name: "Frank".to_string(), age: 55, }, ]; x.sort_by(|a, b| { let base_order = a.age.cmp(&b.age); if reverse { base_order.reverse() } else { base_order } }); // 验证排序结果 for person in x { println!("{}: {}", person.name, person.age); } }
备选方案:用枚举统一返回类型(坚持用sort_by_key)
如果你一定要用sort_by_key,同样可以用枚举来统一返回类型:
use std::cmp::Reverse; struct Person { name: String, age: u32, } enum AgeSortKey<'a> { Direct(&'a u32), Reversed(Reverse<&'a u32>), } impl<'a> Ord for AgeSortKey<'a> { fn cmp(&self, other: &Self) -> std::cmp::Ordering { match (self, other) { (AgeSortKey::Direct(a), AgeSortKey::Direct(b)) => a.cmp(b), (AgeSortKey::Reversed(a), AgeSortKey::Reversed(b)) => a.cmp(b), _ => panic!("Mismatched key types"), } } } impl<'a> PartialOrd for AgeSortKey<'a> { fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { Some(self.cmp(other)) } } impl<'a> PartialEq for AgeSortKey<'a> { fn eq(&self, other: &Self) -> bool { match (self, other) { (AgeSortKey::Direct(a), AgeSortKey::Direct(b)) => a == b, (AgeSortKey::Reversed(a), AgeSortKey::Reversed(b)) => a == b, _ => false, } } } impl<'a> Eq for AgeSortKey<'a> {} fn main() { let reverse = true; let mut x = vec![ Person { name: "Peter".to_string(), age: 18, }, Person { name: "Frank".to_string(), age: 55, }, ]; let key_func = |item: &Person| { if reverse { AgeSortKey::Reversed(Reverse(&item.age)) } else { AgeSortKey::Direct(&item.age) } }; x.sort_by_key(key_func); }
不过显然第一种用sort_by的方案更简洁,不需要额外的枚举和trait实现。
内容的提问来源于stack exchange,提问作者miracle2k
相关产品推荐
相关产品推荐

