zoukankan      html  css  js  c++  java
  • 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>是多线程版本,见线程部分。

  • 相关阅读:
    AtCoder Regular Contest 093
    AtCoder Regular Contest 094
    G. Gangsters in Central City
    HGOI 20190711 题解
    HGOI20190710 题解
    HGOI 20190709 题解
    HGOI 20190708 题解
    HGOI20190707 题解
    HGOI20190706 题解
    HGOI 20190705 题解
  • 原文地址:https://www.cnblogs.com/dream397/p/14185003.html
Copyright © 2011-2022 走看看