rust学习十五.4、Rc和RefCell指针

打印 上一主题 下一主题

主题 911|帖子 911|积分 2733

一、媒介

作为初学者,在只学习了前面几个章节的时间,我以为rust的所有权规则和它说的是一样的。
但现实上,rust发明人并没有遵循这个规则。按照我的想法,应该是因为如果坚持那样大概编写某些代码会太痛楚,甚至大概根本无法实现。
大概根本无法实现这是一个没有去证实的猜想。 不外,我很坚信:如果照旧坚持所有权那一套,大概写某些代码会很痛楚的
在概览了书籍的15章节之后,这是我得出的初步看法。
Rc和RefCell指针就是违反所有权看法的两个指针:
Rc-一份数据可以有多个所有者
RefCell--允许内部修改数据,当又让你外部看起来不可修改(可以通过编译)
无论如何,固然别扭,这些代价照旧可以忍受的,考虑到C++的问题。
二、定义

Rc  -- Reference counter
Rc即引用计数指针,每此增加一个引用,计数+1,反之脱离范围后,引用计数主动减少。当引用计数为0的时间,可以删除这个指针。
RefCell -- Reference Cell
RefCel即引用蜂巢指针,或者是引用指针蜂巢/引用隔间指针,用于封装对内部数据的可变引用。
三、Rc说明及其示例

总之,利用Rc指针,我们可以实现多个变量共享一个Rc指针,实现一些特殊的目的。
但需要注意的是:Rc指针只能用于单线程,或者说不是多线程安全
3.1Rc部分源码

a.定义
  1. #[cfg_attr(not(test), rustc_diagnostic_item = "Rc")]
  2. #[stable(feature = "rust1", since = "1.0.0")]
  3. #[rustc_insignificant_dtor]
  4. pub struct Rc<
  5.     T: ?Sized,
  6.     #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global,
  7. > {
  8.     ptr: NonNull<RcBox<T>>,
  9.     phantom: PhantomData<RcBox<T>>,
  10.     alloc: A,
  11. }
复制代码
 
b.new方法
  1. impl<T> Rc<T> {
  2.     #[cfg(not(no_global_oom_handling))]
  3.     #[stable(feature = "rust1", since = "1.0.0")]
  4.     pub fn new(value: T) -> Rc<T> {
  5.         // There is an implicit weak pointer owned by all the strong
  6.         // pointers, which ensures that the weak destructor never frees
  7.         // the allocation while the strong destructor is running, even
  8.         // if the weak pointer is stored inside the strong one.
  9.         unsafe {
  10.             Self::from_inner(
  11.                 Box::leak(Box::new(RcBox { strong: Cell::new(1), weak: Cell::new(1), value }))
  12.                     .into(),
  13.             )
  14.         }
  15.     }
  16. }
复制代码
 
这个new中有很多不熟悉的内容,需要认真研究下。
但可以看出,这个指针现实至少包罗一个RcBox,包罗了三个部分:strong(强引用),weak(弱引用),value现实的值.
Cell大要上可以看作只有一个值的结构体。
 
c.一个重要的函数clone
  1. #[stable(feature = "rust1", since = "1.0.0")]
  2. impl<T: ?Sized, A: Allocator + Clone> Clone for Rc<T, A> {
  3.     #[inline]
  4.     fn clone(&self) -> Self {
  5.         unsafe {
  6.             self.inner().inc_strong();
  7.             Self::from_inner_in(self.ptr, self.alloc.clone())
  8.         }
  9.     }
  10. }
复制代码
 
self.inner().inc_strong()大要就是增加计数的意思。
这个clone不是一般印象上深度克隆或者浅拷贝之类,功能可以约莫归纳为:引用计数+1
大要上,源码中还有很多不熟悉,不理解,不外不妨碍大要的理解。
更多的内核代码无法给出,因为有些实现,例如Box是通过编译器处理的。
 
3.2 示例

这是模仿书本的示例,两个结构共享一个字符串Rc指针
  1. use std::rc::Rc;
  2. fn main() {
  3.     rc_test();
  4. }
  5. #[derive(Debug)]
  6. struct student{
  7.     name:Rc<String>,
  8.     age:i32,
  9. }
  10. fn  rc_test(){
  11.     let name=Rc::new(String::from("张三"));
  12.     let stu1=student{name:Rc::clone(&name),age:20};
  13.     println!("{:?}",stu1);
  14.     println!("{} 引用次数:{}",name,Rc::strong_count(&name));
  15.     let stu2=student{name:Rc::clone(&name),age:21};
  16.     println!("{:?}",stu2);
  17.     println!("{} 引用次数:{}",name,Rc::strong_count(&name));
  18. }
复制代码
 

 
 
四、RefCell说明及其示例

RefCell 提供了内部可变性,这意味着你可以在不可变上下文中修改数据,而不需要实现 Mutex 或其他同步机制。
它通过运行时借用查抄来实现这一点,而不是编译时查抄。然而,RefCell 也不是线程安全的;它不允许同时从多个线程访问数据
4.1、部分源码
  1. #[cfg_attr(not(test), rustc_diagnostic_item = "RefCell")]
  2. #[stable(feature = "rust1", since = "1.0.0")]
  3. pub struct RefCell<T: ?Sized> {
  4.     borrow: Cell<BorrowFlag>,
  5.     // Stores the location of the earliest currently active borrow.
  6.     // This gets updated whenever we go from having zero borrows
  7.     // to having a single borrow. When a borrow occurs, this gets included
  8.     // in the generated `BorrowError`/`BorrowMutError`
  9.     #[cfg(feature = "debug_refcell")]
  10.     borrowed_at: Cell<Option<&'static crate::panic::Location<'static>>>,
  11.     value: UnsafeCell<T>,
  12. }
复制代码
 
除了value,还有两个属性:borrow,borrowed_at
这两个属性都是基于Cell的,Cell定义如下:
  1. #[stable(feature = "rust1", since = "1.0.0")]
  2. #[repr(transparent)]
  3. #[rustc_pub_transparent]
  4. pub struct Cell<T: ?Sized> {
  5.     value: UnsafeCell<T>,
  6. }
复制代码
 
Cell根据注解,是rust内部可变能力的原生实现
borrow主要标记当前变量借用情况,例如是可变借用照旧不可变借用之类的。
borrow_at,如其名,主要标记在代码中,什么地方发生了借用
这两个标记辅助实现借用查抄。
 
RefCell的两个常用函数:borrow,borrow_mut,分别表示不可变借用和可变借用
我们主要看下borrow_mut
  1.    #[stable(feature = "rust1", since = "1.0.0")]
  2.     #[inline]
  3.     #[track_caller]
  4.     pub fn borrow_mut(&self) -> RefMut<'_, T> {
  5.         match self.try_borrow_mut() {
  6.             Ok(b) => b,
  7.             Err(err) => panic_already_borrowed(err),
  8.         }
  9.     }
复制代码
 

 
五、小结

rust必须屈服于软件工程:即为工程师提供富足的便利性,同时也必须实现一些特定的目标
所以它们推出了Rc和RefCell指针,以便可以实如今别的语言中可以轻易实现的功能。
在别的语言中,希望多个多项共有一个值,或者修改特定对象的内部属性,都是轻而易举的,但是在rust中,变得困难重重。
有了Rc就可以多个共有一个值,有了RefCell就允许修改不可变变量的内部某个值。 Rust为了实现它的目标不得不做出一些牺牲。
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

嚴華

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表