Rust学习(32):智能指针-Rc<T>

 

cat src/main.rs 
use std::rc::Rc;

struct Owner {
    name: String,
    // ...other fields
}

struct Gadget {
    id: i32,
    owner: Rc<Owner>,
    // ...other fields
}

fn main() {
    let gadget_owner: Rc<Owner> = Rc::new(  //堆分配Owner, 产生一个Rc<Owner>计数
        Owner {
            name: "Gadget Man".to_string(),
        }
    );  //现在有一个Owner,在堆中,一个Rc<Owner>, 指针

    let gadget1 = Gadget {
        id: 1,
        owner: Rc::clone(&gadget_owner),  //获得一个指向堆中Owner的Rc<Owner>,计数加一
    };
    let gadget2 = Gadget {
        id: 2,
        owner: Rc::clone(&gadget_owner), //获得指针,计数加一
    };  //现在有一个Owner, 三个Rc<Owner>

    drop(gadget_owner);  //std::mem::drop,销毁一个Rc<Owner>,内存Owner还在

   //剩余两个Rc<Owner>仍然指向Owner
    println!("Gadget {} owned by {}", gadget1.id, gadget1.owner.name); 
    println!("Gadget {} owned by {}", gadget2.id, gadget2.owner.name);

} 

 

 

[root@bogon Rc]# cargo build
   Compiling own v0.1.0 (/data2/rust/Rc)
    Finished dev [unoptimized + debuginfo] target(s) in 0.56s
[root@bogon Rc]# cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/own`
Gadget 1 owned by Gadget Man
Gadget 2 owned by Gadget Man

 

 

 

Rc<T>, 引用计数器,用来记录一个值是否被使用,如果计数为零可清除。适用于堆中数据需要被程序多部分使用,但编译时不能确定谁最后完成。

本质上,Rc<T>类似于这样一个东西:

//pseudo code
{
    let a = some_big_data;
    let b = &a; //counter = 1
    let c = &a; //counter = 2

    {
        let d = &a; //counter = 3
    }  //counter = 2
    
    let d = &a; //counter = 3
}  //counter = 0

Rc<T>通过clone来获得新的Rc<T>指针,增加计数

use std::rc::Rc;
let foo = Rc::new(vec![1.0, 2.0, 3.0]);  //产生一个不可变的引用

// 等价
let a = foo.clone();  //trait,不同于其他类型的clone that is deep copy
let b = Rc::clone(&foo);  
//至此三个Rc<Vec>指向同一个堆中的Vec

//Rc<T>.clone == Rc::clone(&T), 但Rc::clone(&T)为惯用语法,

文档例子

use std::rc::Rc;

struct Owner {
    name: String,
    // ...other fields
}

struct Gadget {
    id: i32,
    owner: Rc<Owner>,
    // ...other fields
}

fn main() {
    let gadget_owner: Rc<Owner> = Rc::new(  //堆分配Owner, 产生一个Rc<Owner>计数
        Owner {
            name: "Gadget Man".to_string(),
        }
    );  //现在有一个Owner,在堆中,一个Rc<Owner>, 指针

    let gadget1 = Gadget {
        id: 1,
        owner: Rc::clone(&gadget_owner),  //获得一个指向堆中Owner的Rc<Owner>,计数加一
    };
    let gadget2 = Gadget {
        id: 2,
        owner: Rc::clone(&gadget_owner), //获得指针,计数加一
    };  //现在有一个Owner, 三个Rc<Owner>

    drop(gadget_owner);  //std::mem::drop,销毁一个Rc<Owner>,内存Owner还在

   //剩余两个Rc<Owner>仍然指向Owner
    println!("Gadget {} owned by {}", gadget1.id, gadget1.owner.name); 
    println!("Gadget {} owned by {}", gadget2.id, gadget2.owner.name);

}  //gadget1, gadget2出局,Rc<Owner>归零,Owner内存释放

Cons list例子

//使用Box<T>, 只允许单个所有者
enum List {
    Cons(i32, Box<List>),
    Nil,
}

use List::{Cons, Nil};

fn main() {  //不能编译
    let a = Cons(5,     //a在栈,Box在栈指向堆中Cons(10)
        Box::new(Cons(10,
            Box::new(Nil)))); //Cons(5, Box) -> Cons(10, Box) -> Nil
    let b = Cons(3, Box::new(a));  //Cons(3, Box) -> a, 夺权。Box在堆中分配内存,放入a
    let c = Cons(4, Box::new(a));  //Cons(4, Box) -> a,无权使用
}

//使用Rc<T>, 允许多个拥有者
enum List {
    Cons(i32, Rc<List>),
    Nil,
}

use List::{Cons, Nil};
use std::rc::Rc;  //需要引入

fn main() {
    let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));  //Cons is List
    //外层Rc::New创建一个Rc<List>, Cons(5, 10)全部在堆
    println!("count after creating a = {}", Rc::strong_count(&a));  //1
    //pub fn strong_count(this: &Rc<T>) -> usize
   
    let b = Cons(3, Rc::clone(&a));  //Rc::clone(&a)只增加引用, Cons(3)在栈, 
    println!("count after creating b = {}", Rc::strong_count(&a));  //2

    {
        let c = Cons(4, Rc::clone(&a));
        println!("count after creating c = {}", Rc::strong_count(&a));  //3
    }  //c出局,Rc<T> Drop特性自动减一

    println!("count after c goes out of scope = {}", Rc::strong_count(&a));  //2
`
} //a, b出局,计数为0,Rc<List>全部清除
//the book这个例子T是递归, List是枚举,搞得很绕,本质上和文档的例子一样
//Box<T>试图直接使用被指向数据产生新的Box以达到共享, Rc<T>通过复制自身即指针来共享

//可以这样写,似乎比较清楚,仅供对比用
let list = Cons(5, Rc::new(Cons(10, Rc::new(Nil)))); //先产生List, Cons(5)在栈,Cons(10)在堆
//let b = Cons(3, Box::new(list))  //如果是Box将重新分配内存?
let a: Rc<List> = Rc::new(list);  //产生第一个Rc指针,似乎不重新分配内存?
let b = Cons(3, Rc::clone(&a));
//如果想在堆中分配内存并立刻开始计数,应该在第一次声明就用Rc::new()

Rc<T>只适用单线程,Arc<T>是多线程版本,见线程部分。

posted on 2020-12-24 16:53  tycoon3  阅读(218)  评论(0)    收藏  举报

导航