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
,另一个叫 CityData
。City
有一个城市的信息,而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() {}
然后用Rc
把String
包围起来:
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)
查询拥有者数量。另外我们再增加一个新的所有者。现在我们的代码是这样的:
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)
来查看弱引用数。