fnmain() { let a = Rc::new(String::from("https://mytechshares.com/")); println!("ref count is {}", Rc::strong_count(&a)); let b = Rc::clone(&a); println!("ref count is {}", Rc::strong_count(&a)); { let c = Rc::clone(&a); println!("ref count is {}", Rc::strong_count(&a)); println!("ref count is {}", Rc::strong_count(&c)); } println!("ref count is {}", Rc::strong_count(&a)); }
strong_count 查看引用计数,变量 c 在 inner 词法作用域内,在离开作用域前打印查看引用计数
1 2 3 4 5 6 7
Finished dev [unoptimized + debuginfo] target(s) in 0.03s Running `target/debug/hello_cargo` ref count is 1 ref count is 2 ref count is 3 ref count is 3 ref count is 2
fnmain() { let first = Rc::new(RefCell::new(Node {next: None})); let second = Rc::new(RefCell::new(Node {next: None})); let third = Rc::new(RefCell::new(Node {next: None})); (*first).borrow_mut().next = Some(Rc::clone(&second)); (*second).borrow_mut().next = Some(Rc::clone(&third)); (*third).borrow_mut().next = Some(Rc::clone(&first)); }
这是一个环形链表的代表,稍微有点绕,原理就是 first -> second -> third, 同时 third -> first, 代码运行后,并没有打印 Dropping https://mytechshares.com 董泽润的技术笔记
PhantomData 不消耗存储空间,它只是模拟了某种类型的数据,以方便静态分析。这么做比显式地告诉类型系统你需要的变性更不容易出错,而且还能提供 drop 检查需要的信息
1 2 3 4 5 6
Zero-sized typeused to mark things that "act like" they own a `T`.
Adding a `PhantomData<T>` field to your typetells the compiler that your typeacts as though it stores a value oftype`T`, even though it doesn't really. This information is used when computing certain safety properties.
简单说 PhantomData 就是零长的占位符,告诉编译器看起来我拥有这个 T, 但不属于我,同时如果析构时,也要调用 drop 释放 T. 因为幽灵字段的存在,Rc 是不拥有 RcBox 的,NonNull 告诉编译器,这个指针 ptr 一定不为空,你可以优化,Option<Rc<T>> 跟 Rc<T> 占用相同的大小,去掉了是否为空的标记字段
1 2 3 4 5 6 7 8 9
pubfnnew(value: T) -> Rc<T> { // There is an implicit weak pointer owned by all the strong // pointers, which ensures that the weak destructor never frees // the allocation while the strong destructor is running, even // if the weak pointer is stored inside the strong one. Self::from_inner( Box::leak(box RcBox { strong: Cell::new(1), weak: Cell::new(1), value }).into(), ) }
impl<T: ?Sized> Rc<T> { #[inline(always)] fninner(&self) -> &RcBox<T> { // This unsafety is ok because while this Rc is alive we're guaranteed // that the inner pointer is valid. unsafe { self.ptr.as_ref() } } }
fninc_strong(&self) { let strong = self.strong();
// We want to abort on overflow instead of dropping the value. // The reference count will never be zero when this is called; // nevertheless, we insert an abort here to hint LLVM at // an otherwise missed optimization. if strong == 0 || strong == usize::MAX { abort(); } self.strong_ref().set(strong + 1); }
// the value usize::MAX acts as a sentinel for temporarily "locking" the // ability to upgrade weak pointers or downgrade strong ones; this is used // to avoid races in `make_mut` and `get_mut`. weak: atomic::AtomicUsize,
data: T, }
而 Arc 里面的计数是用 atomic 来保证原子的,所以是并发字全。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
#[stable(feature = "rc_weak", since = "1.4.0")] pubstructWeak<T: ?Sized> { // This is a `NonNull` to allow optimizing the size of this type in enums, // but it is not necessarily a valid pointer. // `Weak::new` sets this to `usize::MAX` so that it doesn’t need // to allocate space on the heap. That's not a value a real pointer // will ever have because RcBox has alignment at least 2. // This is only possible when `T: Sized`; unsized `T` never dangle. ptr: NonNull<RcBox<T>>, }
#[stable(feature = "rc_weak", since = "1.4.0")] pubfndowngrade(this: &Self) -> Weak<T> { this.inner().inc_weak(); // Make sure we do not create a dangling Weak debug_assert!(!is_dangling(this.ptr.as_ptr())); Weak { ptr: this.ptr } }s