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

如何在Rust中实现可引用其他对象的Prop、Cat与Dict结构?

Rust中只读Dict的实现问题与解决方案

需求概述

  • 定义一组属性(Prop)
  • 定义一组类别(Cat),每个类别由一个或多个Prop组成
  • 实现一个字典(Dict)存储上述内容,要求Prop和Cat在创建后变为只读

用户原代码

use std::collections::HashMap;

struct Prop<'a> {
    name: &'a str
}

type PropVec<'a> = Vec<&'a Prop<'a>>;

struct Cat<'a> {
    name: &'a str,
    props: PropVec<'a>
}

struct Dict<'a> {
    props: HashMap<&'a str, Prop<'a>>,
    cats:  HashMap<&'a str, Cat<'a>>,
}

impl<'a> Dict<'a> {
    pub fn define_prop(&mut self, name: &'a str) {
        self.props.insert(name, Prop { name });
    }

    pub fn prop(&self, name: &str) -> Option<&Prop> {
        self.props.get(name)
    }

    pub fn define_cat(&mut self, name: &'a str, prop_names: Vec<&'a str>) {
        let mut props: PropVec<'a> =  Vec::new();

        for name in prop_names {
            if let Some(prop) = self.prop(name) {
                props.push(prop);
            }
        }
        
        self.cats.insert(name, Cat { name: name, props });
    }
}

fn main() {
    let mut d = Dict { props: HashMap::new(), cats: HashMap::new() };
    d.define_prop("foo");
    d.define_cat("bar", vec!("foo"));
}

编译器报错信息

error: lifetime may not live long enough
  --> src/main.rs:29:24
   |
19 | impl<'a> Dict<'a> {
   |      -- lifetime `'a` defined here
...
28 |     pub fn define_cat(&mut self, name: &'a str, prop_names: Vec<&'a str>) {
   |                       - let's call the lifetime of this reference `'1`
29 |         let mut props: PropVec<'a> =  Vec::new();
   |                        ^^^^^^^^^^^ type annotation requires that `'1` must outlive `'a`

error[E0502]: cannot borrow `self.cats` as mutable because it is also borrowed as immutable
  --> src/main.rs:37:9
   |
19 | impl<'a> Dict<'a> {
   |      -- lifetime `'a` defined here
...
29 |         let mut props: PropVec<'a> =  Vec::new();
   |                        ----------- type annotation requires that `*self` is borrowed for `'a`
...
32 |             if let Some(prop) = self.prop(name) {
   |                                 --------------- immutable borrow occurs here
...
37 |         self.cats.insert(name, Cat { name: name, props });
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here

用户疑问与解答

1. 有没有避免大量生命周期标注的实现方式?正确的生命周期标注方式是什么?

用普通引用的方案本质走不通,因为Rust的借用检查器不允许在持有Prop的不可变引用时,同时对Dict做可变借用(插入Cat)。更合理的方案是使用共享所有权,用Rc<T>存储Prop,这样Dict和Cat都持有Prop的共享所有权,无需生命周期标注。

修正后的实现:

use std::collections::HashMap;
use std::rc::Rc;

#[derive(Clone)]
struct Prop {
    name: String,
}

type PropVec = Vec<Rc<Prop>>;

struct Cat {
    name: String,
    props: PropVec,
}

struct Dict {
    props: HashMap<String, Rc<Prop>>,
    cats: HashMap<String, Cat>,
}

impl Dict {
    pub fn new() -> Self {
        Self {
            props: HashMap::new(),
            cats: HashMap::new(),
        }
    }

    pub fn define_prop(&mut self, name: &str) {
        let prop = Rc::new(Prop { name: name.to_string() });
        self.props.insert(name.to_string(), prop);
    }

    pub fn prop(&self, name: &str) -> Option<Rc<Prop>> {
        self.props.get(name).cloned()
    }

    pub fn define_cat(&mut self, name: &str, prop_names: Vec<&str>) {
        let mut props = Vec::new();
        for prop_name in prop_names {
            if let Some(prop) = self.prop(prop_name) {
                props.push(prop);
            }
        }
        self.cats.insert(name.to_string(), Cat { name: name.to_string(), props });
    }
}

fn main() {
    let mut d = Dict::new();
    d.define_prop("foo");
    d.define_cat("bar", vec!("foo"));
    
    // 创建完成后转为不可变引用,实现只读
    let d = d;
    // 此时无法再调用define_prop或define_cat
}

如果坚持用引用方案,生命周期标注无法解决根本的借用冲突——在define_cat中,先不可变借用self获取Prop引用,之后又要可变借用self插入Cat,这违反了Rust“同一时间不能同时存在可变和不可变借用”的规则。

2. 第29行关于*self的注释需要关注吗?

需要关注。这条错误说明编译器认为self的临时借用生命周期('1)需要比Dict的生命周期('a)更长,但实际上&mut self的生命周期仅存在于函数调用期间,不可能覆盖Dict的整个生命周期,这是引用方案无法调和的矛盾,也是必须换用所有权方案的核心原因。

3. 能否获取Prop或其引用并存到其他地方?

用普通引用的话不行,因为Rust的借用规则禁止在持有Dict可变引用时同时持有内部Prop的不可变引用。但用Rc<Prop>的话,完全可以通过克隆Rc将Prop的共享所有权存储到Cat或其他地方,而且Prop的字段是不可变的(用String而非&str,且无修改方法),天然满足创建后只读的需求。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 08:27:05