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

如何复用HashMap持有的值的引用?Rust递归类型借用问题求解

错误原因

Rust的借用检查器严格禁止同一变量同时存在可变借用与不可变借用。你通过table.get(&Nil)获取了table内部Nil实例的不可变引用,此时table处于不可变借用状态;后续调用table.insert需要对table进行可变借用,且插入参数还依赖之前的不可变引用nil。Rust无法保证可变借用(修改HashMap)过程中旧的不可变引用仍有效(比如HashMap扩容可能移动内部元素,导致旧引用失效),因此触发了借用冲突错误。

实现等价对象引用唯一的方案

要实现“等价对象引用完全一致”的需求,最常用的是字符串池(interner)模式,以下提供两种可行方案:

方案1:用Arc结合Interner(安全且简单)

虽然你最初想避免智能指针,但Arc可以安全实现引用一致性,且符合Rust内存安全规则。我们可以实现专门的ListInterner,确保每个等价List实例仅创建一次,后续均返回同一个Arc的引用:

use std::collections::HashMap;
use std::sync::Arc;

#[derive(Eq, Hash, PartialEq, Clone)]
enum List {
    Cons(isize, Arc<List>),
    Nil,
}

struct ListInterner {
    map: HashMap<List, Arc<List>>,
}

impl ListInterner {
    fn new() -> Self {
        let mut map = HashMap::new();
        let nil = Arc::new(List::Nil);
        map.insert(List::Nil, nil.clone());
        Self { map }
    }

    // 获取唯一的List引用,等价实例返回的Arc底层指针一致
    fn get(&mut self, list: List) -> Arc<List> {
        if let Some(existing) = self.map.get(&list) {
            existing.clone()
        } else {
            let arc = Arc::new(list.clone());
            self.map.insert(list, arc.clone());
            arc
        }
    }
}

fn main() {
    use List::*;
    let mut interner = ListInterner::new();
    
    let nil = interner.get(Nil);
    let cons1 = interner.get(Cons(1, nil.clone()));
    
    // 验证引用一致性:多次获取同一List,Arc底层指针相同
    let cons1_again = interner.get(Cons(1, nil));
    assert!(Arc::ptr_eq(&cons1, &cons1_again));
}

通过Arc::ptr_eq可直接验证底层实例的指针是否一致,满足你后续类似std::ptr::eq的需求。

方案2:无智能指针的unsafe实现(需手动保证内存安全)

若坚持不用智能指针,需手动管理内存并确保引用不会失效。可以用Vec存储所有唯一实例(仅允许向末尾添加元素,禁止删除或移动),结合HashMap映射值到索引,再通过索引获取引用:

use std::collections::HashMap;

#[derive(Eq, Hash, PartialEq, Clone, Copy)]
enum ListIdx {
    Cons(isize, usize),
    Nil,
}

#[derive(Eq, Hash, PartialEq)]
enum List {
    Cons(isize, &'static List),
    Nil,
}

struct ListInterner {
    storage: Vec<Box<List>>,
    map: HashMap<ListIdx, usize>,
}

impl ListInterner {
    fn new() -> Self {
        let mut storage = Vec::new();
        let mut map = HashMap::new();
        
        // 初始化Nil实例
        let nil = Box::new(List::Nil);
        let nil_ptr = &*nil as *const List;
        storage.push(nil);
        map.insert(ListIdx::Nil, 0);
        
        // 强制转换为'static,需保证storage仅添加元素、绝不修改已有元素
        unsafe {
            let _ = &*nil_ptr as &'static List;
        }
        
        Self { storage, map }
    }

    fn get(&mut self, idx: ListIdx) -> &'static List {
        if let Some(&index) = self.map.get(&idx) {
            unsafe {
                &*self.storage[index] as &'static List
            }
        } else {
            let list = match idx {
                ListIdx::Cons(val, inner_idx) => {
                    let inner = self.get(ListIdx::Nil);
                    List::Cons(val, inner)
                }
                ListIdx::Nil => List::Nil,
            };
            let boxed = Box::new(list);
            let ptr = &*boxed as *const List;
            let index = self.storage.len();
            self.storage.push(boxed);
            self.map.insert(idx, index);
            unsafe {
                &*ptr as &'static List
            }
        }
    }
}

fn main() {
    let mut interner = ListInterner::new();
    let nil = interner.get(ListIdx::Nil);
    let cons1 = interner.get(ListIdx::Cons(1, 0));
    
    // 验证引用一致性
    let cons1_again = interner.get(ListIdx::Cons(1, 0));
    assert!(std::ptr::eq(cons1, cons1_again));
}

注意:此方案使用unsafe,必须严格保证storage仅向末尾添加元素,绝不删除或移动已有元素,否则'static引用会变成悬垂引用,引发未定义行为。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 09:35:24