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

Rust中如何实现持有结构体所有权的Vec与引用该结构体的Vec共存?

Rust中如何实现持有结构体所有权的Vec与引用该结构体的Vec共存?

这个问题简直是Rust新手绕不开的借用规则“坑”!我当初刚学的时候也卡了好久,给你几个实用的解决方案,你可以根据自己的实际业务场景来选:

方案一:用共享所有权智能指针(Rc/Arc)彻底避开借用问题

这是最通用也最安全的方案,核心思路是让people和events都持有Person的共享所有权,而不是用引用。Rust提供了Rc(单线程)和Arc(多线程)两个智能指针来实现这个功能,它们会帮你管理引用计数,当最后一个所有者消失时,Person才会被销毁。

修改后的代码示例:

use std::rc::Rc;

struct Person {
    name: String
}

enum Event {
    PersonBorn(i32, Rc<Person>),
    PersonDied(i32, Rc<Person>),
}

struct Universe {
    people: Vec<Rc<Person>>,
    events: Vec<Event>
}

fn main() {
    let mut data = Universe {
        people: Vec::new(),
        events: Vec::new()
    };

    let names = ["John", "Joe"];

    for name in names {
        let year = 1993;

        // 用Rc包裹Person,创建第一个所有者
        let person = Rc::new(Person {
            name: String::from(name)
        });
        // 克隆Rc(只是增加引用计数,不会复制Person的实际数据)
        data.people.push(Rc::clone(&person));
        // 把原始的Rc传给Event
        data.events.push(Event::PersonBorn(year, person));
    }

    for event in data.events {
        match event {
            Event::PersonBorn(year, person) => println!("{} was born in {}", person.name, year),
            Event::PersonDied(year, person) => println!("{} died in {}", person.name, year),
        }
    }
}

这个方案的优点是完全不受借用规则限制,你可以随意修改people和events,唯一的小代价是一点点引用计数的性能开销(几乎可以忽略不计)。如果你的程序是多线程的,把Rc换成Arc就行。

方案二:用索引代替引用(适合集合固定的场景)

如果你的people集合创建后就不会再修改(比如不会push新元素、不会删除/插入元素),那用索引代替引用会更高效,不需要额外的智能指针。

代码示例:

struct Person {
    name: String
}

enum Event {
    PersonBorn(i32, usize),
    PersonDied(i32, usize),
}

struct Universe {
    people: Vec<Person>,
    events: Vec<Event>
}

fn main() {
    let mut data = Universe {
        people: Vec::new(),
        events: Vec::new()
    };

    let names = ["John", "Joe"];

    for name in names {
        let year = 1993;

        let person = Person {
            name: String::from(name)
        };
        // 先拿到即将插入的索引(因为push后长度会加1,当前长度就是新元素的索引)
        let index = data.people.len();
        data.people.push(person);
        // 把索引存入Event
        data.events.push(Event::PersonBorn(year, index));
    }

    for event in data.events {
        match event {
            Event::PersonBorn(year, index) => {
                let person = &data.people[index];
                println!("{} was born in {}", person.name, year)
            },
            Event::PersonDied(year, index) => {
                let person = &data.people[index];
                println!("{} died in {}", person.name, year)
            },
        }
    }
}

⚠️ 注意:这个方案有个致命限制——如果之后你对people做了会导致元素移动的操作(比如push新元素触发Vec扩容、insert/remove元素),之前的索引就会失效,访问时会导致错误。所以只适合people固定不变的场景。

方案三:调整代码执行顺序(如果业务允许的话)

虽然你说尝试3在实际场景不可行,但还是提一下:如果能先收集所有Person和对应的事件信息,再分别填充people和events,也能解决问题。比如先创建一个临时的Vec存储(Person, 年份),然后遍历这个临时Vec,把Person放到people,同时生成Event放到events。不过既然你说实际场景不允许,这个就当备选思路吧。

总结一下:如果你的场景需要灵活修改集合,优先选Rc/Arc方案;如果集合固定不变,用索引更高效。

备注:内容来源于stack exchange,提问作者Jouwee

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 10:18:07