如何解决Rust中自定义trait无法实现Ord及trait object适配问题
为什么你的代码会报错
你遇到的两个错误本质都是因为Ord trait不符合对象安全要求:
Ord继承自PartialOrd<Self>,而PartialOrd的方法参数用了Self类型——动态分发(trait object)时,编译器无法确定具体的Self类型,没法生成用于动态调用的虚表(vtable),所以Ord不能被做成trait object。- Rust的trait object只能有一个主 trait,额外附加的只能是自动 trait(比如
Send、Sync这种无需手动实现的 trait),所以你不能把Ord和Cookie一起放在Box<dyn ...>里。
两种可行的实现方案
方案一:基于自定义排序key直接排序(推荐)
不需要依赖Ord trait object,而是给你的Cookie trait添加一个返回排序用key的方法,排序时直接用这个key来比较:
use itertools::Itertools; use std::collections::HashMap; // 你的自定义Cookie trait,新增sort_key方法 pub trait Cookie { fn sort_key(&self) -> &str; // 保留你原有的其他方法,比如value() fn value(&self) -> &str; } #[derive(Default, Debug)] pub struct CookieJar { _inner: HashMap<String, Box<dyn Cookie>>, } impl CookieJar { // 提供一个返回排序后值的方法 pub fn sorted_values(&self) -> impl Iterator<Item = &Box<dyn Cookie>> { // 用sort_key作为排序依据 self._inner.values().sorted_by_key(|cookie| cookie.sort_key().to_string()) } }
这样调用cookie_jar.sorted_values()就能得到按sort_key排序后的迭代器,完全满足需求。
方案二:用枚举封装所有Cookie类型(适合已知所有Cookie实现的场景)
如果你的项目中所有实现Cookie的类型都是已知的,可以用枚举来统一封装,然后给枚举实现Ord trait:
use itertools::Itertools; use std::collections::HashMap; use std::cmp::{Ord, Ordering, PartialOrd, PartialEq, Eq}; // 你的自定义Cookie trait pub trait Cookie { fn sort_key(&self) -> &str; fn value(&self) -> &str; } // 枚举所有可能的Cookie实现类型 pub enum CookieVariant { ChromeCookie(ChromeCookie), FirefoxCookie(FirefoxCookie), } // 给枚举实现Cookie trait impl Cookie for CookieVariant { fn sort_key(&self) -> &str { match self { CookieVariant::ChromeCookie(c) => c.sort_key(), CookieVariant::FirefoxCookie(c) => c.sort_key(), } } fn value(&self) -> &str { match self { CookieVariant::ChromeCookie(c) => c.value(), CookieVariant::FirefoxCookie(c) => c.value(), } } } // 给枚举实现Ord及相关trait impl Ord for CookieVariant { fn cmp(&self, other: &Self) -> Ordering { self.sort_key().cmp(other.sort_key()) } } impl PartialOrd for CookieVariant { fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) } } impl PartialEq for CookieVariant { fn eq(&self, other: &Self) -> bool { self.sort_key() == other.sort_key() } } impl Eq for CookieVariant {} #[derive(Default, Debug)] pub struct CookieJar { _inner: HashMap<String, CookieVariant>, } impl CookieJar { pub fn sorted_values(&self) -> impl Iterator<Item = &CookieVariant> { // 直接调用itertools的sorted() self._inner.values().sorted() } } // 示例Cookie实现 struct ChromeCookie { key: String, value: String, } impl Cookie for ChromeCookie { fn sort_key(&self) -> &str { &self.key } fn value(&self) -> &str { &self.value } } struct FirefoxCookie { key: String, value: String, } impl Cookie for FirefoxCookie { fn sort_key(&self) -> &str { &self.key } fn value(&self) -> &str { &self.value } }
这种方案的好处是可以直接使用itertools::sorted(),缺点是每次新增Cookie类型都要修改枚举。
内容的提问来源于stack exchange,提问作者Evan Carroll
相关产品推荐
相关产品推荐

