rust, smart pointer
Box<>
Like unique_ptr in C++.
Internally, it contains a Unqiue<> which holds the raw pointer.
pub struct Box<T: ?Sized>(Unique<T>);
pub struct Unique<T: ?Sized> {
pointer: *const T,
// NOTE: this marker has no consequences for variance, but is necessary
// for dropck to understand that we logically own a `T`.
//
// For details, see:
// https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md#phantom-data
_marker: PhantomData<T>,
}
Box::new
It uses ‘box’ to allocate T from heap.
‘box’ -> exchange_malloc by compiler.
pub fn new(x: T) -> Box<T> {
box x
}
Rc<>
Like shared_ptr in C++. It implements the reference-counting pointer for single-threaded environment.
pub struct Rc<T: ?Sized> {
ptr: NonNull<RcBox<T>>,
phantom: PhantomData<RcBox<T>>,
}
pub struct NonNull<T: ?Sized> {
pointer: *const T,
}
struct RcBox<T: ?Sized> {
strong: Cell<usize>,
weak: Cell<usize>,
value: T,
}
RcBox holds counters for strong and weak references. Note these two counters are Cell<> which means it is interior mutable.
Rc.new
RcBox is created by ‘box’ to hold the value and counters.
pub fn new(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::into_raw_non_null(box RcBox {
strong: Cell::new(1),
weak: Cell::new(1),
value,
}))
}
Weak<>
Weak
is a version of [Rc
] that holds a non-owning reference to the managed allocation. The allocation is accessed by calling [upgrade
] on the Weak
pointer, which returns an [Option
]<
[Rc
]<T>>
.
pub struct Weak<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.
ptr: NonNull<RcBox<T>>,
}
pub fn upgrade(&self) -> Option<Rc<T>> {
let inner = self.inner()?;
if inner.strong() == 0 {
None
} else {
inner.inc_strong();
Some(Rc::from_inner(self.ptr))
}
}
pub fn downgrade(this: &Self) -> Weak<T> {
this.inc_weak();
// Make sure we do not create a dangling Weak
debug_assert!(!is_dangling(this.ptr));
Weak { ptr: this.ptr }
}
Cell<>
Cell is a mutable memory location which means mutation inside an immutable struct, or interior mutability. Internally it contains a UnsafeCell<> which is a wrap to the T.
Strictly it is not a pointer.
pub struct Cell<T: ?Sized> {
value: UnsafeCell<T>,
}
Cell.new
It creates a new Cell
containing the given value.
pub const fn new(value: T) -> Cell<T> {
Cell { value: UnsafeCell::new(value) }
}
Cell get/set
set will replace the value with new val and drop the old.
get will simple return the copy of the value. Note ‘get’ is under a constraint Copy trait.
pub fn set(&self, val: T) {
let old = self.replace(val);
drop(old);
}
pub fn replace(&self, val: T) -> T {
// SAFETY: This can cause data races if called from a separate thread,
// but `Cell` is `!Sync` so this won't happen.
mem::replace(unsafe { &mut *self.value.get() }, val)
}
pub fn get(&self) -> T {
// SAFETY: This can cause data races if called from a separate thread,
// but `Cell` is `!Sync` so this won't happen.
unsafe { *self.value.get() }
}
RefCell<>
RefCell is a mutable memory location with dynamically checked borrow rules. Not like Cell<>, borrow rules are checked at runtime. In this case, it will panic!() if any violations found.
Roughly, RefCell<> is just like Cell<>, but provides reference/de-reference. To get access to the value in RefCell, borrow/borrow_mut must be used.
BorrowFlag is used to track the borrow relationship, and value is held by UnsafeCell<>.
pub struct RefCell<T: ?Sized> {
borrow: Cell<BorrowFlag>,
value: UnsafeCell<T>,
}
RefCell borrow/borrow_mut
pub fn borrow_mut(&self) -> RefMut<'_, T> {
self.try_borrow_mut().expect("already borrowed")
}
pub fn borrow(&self) -> Ref<'_, T> {
self.try_borrow().expect("already mutably borrowed")
}
Arc<>
A thread-safe reference-counting pointer. ‘Arc’ stands for ‘Atomically Reference Counted’.
Internally it uses ArcInner to hold the value and counters.
pub struct Arc<T: ?Sized> {
ptr: NonNull<ArcInner<T>>,
phantom: PhantomData<ArcInner<T>>,
}
struct ArcInner<T: ?Sized> {
strong: atomic::AtomicUsize,
// 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::new
Use box
to allocate memory for ArcInner.
pub fn new(data: T) -> Arc<T> {
// Start the weak pointer count as 1 which is the weak pointer that's
// held by all the strong pointers (kinda), see std/rc.rs for more info
let x: Box<_> = box ArcInner {
strong: atomic::AtomicUsize::new(1),
weak: atomic::AtomicUsize::new(1),
data,
};
Self::from_inner(Box::into_raw_non_null(x))
}
Mutex<>
Mutex is a mutual exclusion primitive useful for protecting shared data.
pub struct Mutex<T: ?Sized> {
// Note that this mutex is in a *box*, not inlined into the struct itself.
// Once a native mutex has been used once, its address can never change (it
// can't be moved). This mutex type can be safely moved at any time, so to
// ensure that the native mutex is used correctly we box the inner mutex to
// give it a constant address.
inner: Box<sys::Mutex>,
poison: poison::Flag,
data: UnsafeCell<T>,
}
The sys::Mutex is boxed as shown above.
Mutex::new
inner is a boxed sys::Mutex.
pub fn new(t: T) -> Mutex<T> {
let mut m = Mutex {
inner: box sys::Mutex::new(),
poison: poison::Flag::new(),
data: UnsafeCell::new(t),
};
unsafe {
m.inner.init();
}
m
}
Mutex lock/trylock/drop
drop will unlock the mutex.
pub fn lock(&self) -> LockResult<MutexGuard<'_, T>> {
unsafe {
self.inner.raw_lock();
MutexGuard::new(self)
}
}
pub fn try_lock(&self) -> TryLockResult<MutexGuard<'_, T>> {
unsafe {
if self.inner.try_lock() {
Ok(MutexGuard::new(self)?)
} else {
Err(TryLockError::WouldBlock)
}
}
}
fn drop(&mut self) {
// This is actually safe b/c we know that there is no further usage of
// this mutex (it's up to the user to arrange for a mutex to get
// dropped, that's not our job)
//
// IMPORTANT: This code must be kept in sync with `Mutex::into_inner`.
unsafe { self.inner.destroy() }
}