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>是多线程版本,见线程部分。

  • 相关阅读:
    ubuntu set/unset proxy
    Caffe Ubuntu14.04 64位 的最快安装 (cuda7.5 + cudnn7.0 2016最新)
    OpenCV3.0.0+win10 64位+vs2015环境的下载,安装,配置
    Hadoop学习第一天
    PDF在xp或2003下正常在win7下乱码的问题
    android入门到熟练(五)---广播
    android入门到熟练(三)----UI界面
    android入门到熟练(二)----活动
    android入门到熟练(一)
    jQuery慢慢啃之回调(十三)
  • 原文地址:https://www.cnblogs.com/dream397/p/14185003.html
Copyright © 2011-2022 走看看