46-Rc

Rc — Easy Rust 中文译本

译文 · 基于 Easy Rust

原文链接: https://dhghomon.github.io/easy_rust/Chapter_45.html

中文参考:kumakichi/easy_rust_chs

Rc

Rc的意思是 “reference counter”(引用计数器)。你知道在Rust中,每个变量只能有一个所有者。这就是为什么这个不能工作的原因:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
fn takes_a_string(input: String) {
    println!("It is: {}", input)
}

fn also_takes_a_string(input: String) {
    println!("It is: {}", input)
}

fn main() {
    let user_name = String::from("User MacUserson");

    takes_a_string(user_name);
    also_takes_a_string(user_name); // ⚠️
}

takes_a_string取了user_name之后,你就不能再使用了。这里没有问题:你可以直接给它user_name.clone()。但有时一个变量是一个结构的一部分,也许你不能克隆这个结构;或者String真的很长,你不想克隆它。这些都是Rc的一些原因,它让你拥有多个所有者。Rc就像一个优秀的办公人员。Rc写下谁拥有所有权,以及有多少个。然后一旦所有者的数量下降到0,这个变量就可以消失了。

下面是如何使用Rc。首先想象两个结构:一个叫 City,另一个叫 CityData。City有一个城市的信息,而CityData把所有的城市都放在Vec中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#[derive(Debug)]
struct City {
    name: String,
    population: u32,
    city_history: String,
}

#[derive(Debug)]
struct CityData {
    names: Vec<String>,
    histories: Vec<String>,
}

fn main() {
    let calgary = City {
        name: "Calgary".to_string(),
        population: 1_200_000,
           // 假装这个字符串非常非常长
        city_history: "Calgary began as a fort called Fort Calgary that...".to_string(),
    };

    let canada_cities = CityData {
        names: vec![calgary.name], // 这里用的是 calgary.name,比较短
        histories: vec![calgary.city_history], // 但这个 String 很长
    };

    println!("Calgary's history is: {}", calgary.city_history);  // ⚠️
}

当然,这是不可能的,因为canada_cities现在拥有数据,而calgary没有。它说:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
error[E0382]: borrow of moved value: `calgary.city_history`
  --> src\main.rs:27:42
   |
24 |         histories: vec![calgary.city_history], // 但这个 String 很长
   |                         -------------------- value moved here
...
27 |     println!("Calgary's history is: {}", calgary.city_history);  // ⚠️
   |                                          ^^^^^^^^^^^^^^^^^^^^ value borrowed here after move
   |
   = note: move occurs because `calgary.city_history` has type `std::string::String`, which does not implement the `Copy` trait

我们可以克隆名称:names: vec![calgary.name.clone()],但是我们不想克隆city_history,因为它很长。所以我们可以用一个Rc。

增加use的声明。

1
2
3
use std::rc::Rc;

fn main() {}

然后用Rc把String包围起来:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
use std::rc::Rc;

#[derive(Debug)]
struct City {
    name: String,
    population: u32,
    city_history: Rc<String>,
}

#[derive(Debug)]
struct CityData {
    names: Vec<String>,
    histories: Vec<Rc<String>>,
}

fn main() {}

要添加一个新的引用,你必须clone Rc。但是等一下,我们不是想避免使用.clone()吗?不完全是:我们不想克隆整个String。但是一个Rc的克隆只是克隆了指针–它基本上是没有开销的。这就像在一盒书上贴上一个名字贴纸,以表明有两个人拥有它,而不是做一盒全新的书。

你可以用item.clone()或者用Rc::clone(&item)来克隆一个叫item的Rc。所以calgary.city_history有两个所有者。 我们可以用Rc::strong_count(&item)查询拥有者数量。另外我们再增加一个新的所有者。现在我们的代码是这样的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
use std::rc::Rc;

#[derive(Debug)]
struct City {
    name: String,
    population: u32,
    city_history: Rc<String>, // Rc 里的 String
}

#[derive(Debug)]
struct CityData {
    names: Vec<String>,
    histories: Vec<Rc<String>>, // 装在 Rc 里的 String 的 Vec
}

fn main() {
    let calgary = City {
        name: "Calgary".to_string(),
        population: 1_200_000,
           // 假装这个字符串非常非常长
        city_history: Rc::new("Calgary began as a fort called Fort Calgary that...".to_string()), // 用 Rc::new() 创建 Rc
    };

    let canada_cities = CityData {
        names: vec![calgary.name],
        histories: vec![calgary.city_history.clone()], // .clone() 来增加计数
    };

    println!("Calgary's history is: {}", calgary.city_history);
    println!("{}", Rc::strong_count(&calgary.city_history));
    let new_owner = calgary.city_history.clone();
}

这就打印出了2。而new_owner现在是Rc<String>。现在如果我们用println!("{}", Rc::strong_count(&calgary.city_history));,我们得到3。

那么,如果有强指针,是否有弱指针呢?是的,有。弱指针是有用的,因为如果两个Rc互相指向对方,它们就不会死。这就是所谓的 “引用循环”。如果第1项对第2项有一个Rc,而第2项对第1项有一个Rc,它们不能到0,在这种情况下,要使用弱引用。那么Rc就会对引用进行计数,但如果只有弱引用,那么它就会死掉。你使用Rc::downgrade(&item)而不是Rc::clone(&item)来创建弱引用。另外,需要用Rc::weak_count(&item)来查看弱引用数。

最后修改 August 21, 2026: 更新 (76fc81a2e)