You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust中如何构造无需T实现Hash/Ord的Rc<T>集合实现去重?

解决方案

标准库原生实现方案

你的理解没有问题:Rc指向的堆内存地址只要对应实例没有被销毁就不会发生变化,完全可以基于地址做哈希、判等、排序,不需要依赖T的任何trait实现。
标准库没有直接提供基于地址判重的Rc集合,但你可以通过NewType模式自定义包装类型,手动实现Hash、PartialEq、Eq(需要HashSet的场景),额外实现Ord、PartialOrd(需要BTreeSet的场景)即可,代码如下:

use std::rc::Rc;
use std::hash::{Hash, Hasher};
use std::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};

#[derive(Debug, Clone, Eq)]
struct RcByAddr<T>(Rc<T>);

impl<T> PartialEq for RcByAddr<T> {
    fn eq(&self, other: &Self) -> bool {
        // 直接判断两个Rc是否指向同一个堆地址
        Rc::ptr_eq(&self.0, &other.0)
    }
}

impl<T> Hash for RcByAddr<T> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        // 基于堆地址做哈希
        Rc::as_ptr(&self.0).hash(state)
    }
}

// 如需使用BTreeSet,补充以下两个实现
impl<T> Ord for RcByAddr<T> {
    fn cmp(&self, other: &Self) -> Ordering {
        Rc::as_ptr(&self.0).cmp(&Rc::as_ptr(&other.0))
    }
}

impl<T> PartialOrd for RcByAddr<T> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

使用示例:

use std::collections::HashSet;

struct Foo {
    // 任意成员,不需要实现Hash/Ord
}

fn main() {
    let a = Rc::new(Foo {});
    let b = Rc::clone(&a);
    let c = Rc::new(Foo {});

    let mut set = HashSet::new();
    set.insert(RcByAddr(a.clone()));
    
    assert!(set.contains(&RcByAddr(b))); // 指向同一个地址,返回true
    assert!(!set.contains(&RcByAddr(c))); // 指向不同地址,返回false
}

注意事项

  • 该实现完全安全:RcByAddr持有Rc<T>的所有权,会保证对应堆内存不会被释放、地址不会被复用,不存在地址重用导致的判重错误。
  • 标准库默认的Rc<T>的Hash、Eq实现是转发给内部T的,所以你之前直接用HashSet<Rc<Foo>>才会要求Foo实现对应trait,自定义包装类型相当于重写了这部分逻辑,完全符合你的需求。
  • 不需要额外引入第三方库,上面的自定义实现仅几十行代码,无额外依赖。

内容的提问来源于stack exchange,提问作者aedm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 03:24:03