Rc

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

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,另一个叫 CityDataCity有一个城市的信息,而CityData把所有的城市都放在Vec中。

#[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,
           // Pretend that this string is very very long
        city_history: "Calgary began as a fort called Fort Calgary that...".to_string(),
    };

    let canada_cities = CityData {
        names: vec![calgary.name], // This is using calgary.name, which is short
        histories: vec![calgary.city_history], // But this String is very long
    };

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

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

error[E0382]: borrow of moved value: `calgary.city_history`
  --> src\main.rs:27:42
   |
24 |         histories: vec![calgary.city_history], // But this String is very long
   |                         -------------------- 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的声明。

use std::rc::Rc;

fn main() {}

然后用RcString包围起来:

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)来克隆一个叫itemRc。所以calgary.city_history有两个所有者。 我们可以用Rc::strong_count(&item)查询拥有者数量。另外我们再增加一个新的所有者。现在我们的代码是这样的:

use std::rc::Rc;

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

#[derive(Debug)]
struct CityData {
    names: Vec<String>,
    histories: Vec<Rc<String>>, // A Vec of Strings inside Rcs
}

fn main() {
    let calgary = City {
        name: "Calgary".to_string(),
        population: 1_200_000,
           // Pretend that this string is very very long
        city_history: Rc::new("Calgary began as a fort called Fort Calgary that...".to_string()), // Rc::new() to make the Rc
    };

    let canada_cities = CityData {
        names: vec![calgary.name],
        histories: vec![calgary.city_history.clone()], // .clone() to increase the count
    };

    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)来查看弱引用数。