智能指针

Rust 中, 凡是需要做资源回收的数据结构, 且实现了 Deref/DerefMut/Drop, 都是智能指针.

#![allow(unused)]
fn main() {
pub trait Deref {
    type Target: ?Sized;

    fn deref(&self) -> &Self::Target;
}
pub trait DerefMut: Deref {
    fn deref_mut(&mut self) -> &mut Self::Target;
}
pub trait Drop {
    fn drop(&mut self);
}
}

1. String 和 &str

智能指针一定是一个胖指针, 但胖指针不一定是一个智能指针, 比如 &str 就只是一个胖指针, 它有指向堆内存字符串的指针, 同时还有关于字符串长度的元数据.

智能指针 String&str 的区别:

image-20230115191903977

String 对堆上的值有所有权, 而 &str 是没有所有权的, 这是 Rust智能指针普通胖指针的区别.

String 是用结构体定义的.

#![allow(unused)]
fn main() {
pub struct String {
    vec: Vec<u8>,
}
}

和普通的结构体不同的是, String 实现了 DerefDerefMut, 这使得它在解引用的时候, 会得到 &str

#![allow(unused)]
fn main() {
impl ops::Deref for String {
    type Target = str;

    fn deref(&self) -> &str {
        unsafe { str::from_utf8_unchecked(&self.vec) }
    }
}

impl ops::DerefMut for String {
    fn deref_mut(&mut self) -> &mut str {
        unsafe { str::from_utf8_unchecked_mut(&mut *self.vec) }
    }
}
}

由于在堆上分配了数据, String 还需要为其分配的资源做相应的回收. 而 String 内部使用了 Vec, 所以它可以依赖 Vec 的能力来释放堆内存.

#![allow(unused)]
fn main() {
unsafe impl<#[may_dangle] T, A: Allocator> Drop for Vec<T, A> {
    fn drop(&mut self) {
        unsafe {
            // use drop for [T]
            // use a raw slice to refer to the elements of the vector as weakest necessary type;
            // could avoid questions of validity in certain cases
            ptr::drop_in_place(ptr::slice_from_raw_parts_mut(self.as_mut_ptr(), self.len))
        }
        // RawVec handles deallocation
    }
}
}

2. Box<T>

Box<T> 的定义里, 内部就是一个 Unique<T> 用于致敬 C++, Unique<T> 是一个私有的数据结构, 我们不能直接使用, 它包裹了一个 *const T 指针, 并唯一拥有这个指针.

#![allow(unused)]
fn main() {
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>,
}

pub struct Box<T: ?Sized,A: Allocator = Global>(Unique<T>, A)
}

3. Cow<'a, B>

CowRust 下用于提供写时克隆 (Clone-on-Write) 的一个智能指针, 它跟虚拟内存管理的写时复制 (Copy-on-write) 有异曲同工之妙: **包裹一个只读借用, 但如果调用者需要所有权或者需要修改内容, 那么它会 clone 借用的数据. **

#![allow(unused)]
fn main() {
pub enum Cow<'a, B> 
	where B: 'a + ToOwned + ?Sized {
  Borrowed(&'a B),  			// 对类型 B 的只读引用
  Owned(<B as ToOwned>::Owned), // 包含对类型 B 的拥有所有权的数据
}
}

这里引入了两个 trait, 首先是ToOwned, 在ToOwner trait 定义的时候, 又引入了 Borrow trait, 它们都是 std::borrow 下的 trait.

#![allow(unused)]
fn main() {
pub trait ToOwned {
    type Owned: Borrow<Self>;
    #[must_use = "cloning is often expensive and is not expected to have side effects"]
    fn to_owned(&self) -> Self::Owned;

    fn clone_into(&self, target: &mut Self::Owned) { ... }
}

pub trait Borrow<Borrowed> where Borrowed: ?Sized {
    fn borrow(&self) -> &Borrowed;
}
}

首先 Owned: Borrow<Self> 是一个带有关联类型的 trait, 这里的 Owned 关联类型, 需要使用者定义, 并且这里的Owned 不能是任意类型, 它必须满足 Borrow<T> trait. 例如 strToOwner trait 的实现:

#![allow(unused)]
fn main() {
impl ToOwned for str {
    type Owned = String;
    #[inline]
    fn to_owned(&self) -> String {
        unsafe { String::from_utf8_unchecked(self.as_bytes().to_owned()) }
    }

    fn clone_into(&self, target: &mut String) {
        let mut b = mem::take(target).into_bytes();
        self.as_bytes().clone_into(&mut b);
        *target = unsafe { String::from_utf8_unchecked(b) }
    }
}
}

可以看到关联类型 Owned 被定义为 String, 而根据要求, String 必须定义 Borrow, 那这里 Borrow 里的泛型变量 T 是谁呢?

ToOwned 要求是 Borrow, 而此刻实现 ToOwned 的主体是 str, 所以 BorrowBorrow<str>, 也就是说 String 要实现 Borrow<str>, 我们看文档, 它的确实现了这个 trait:

#![allow(unused)]
fn main() {
impl Borrow<str> for String {
    #[inline]
    fn borrow(&self) -> &str {
        &self[..]
    }
}
}

Cow说它是智能指针, 那它自然需要实现 Deref trait:

#![allow(unused)]
fn main() {
impl<B: ?Sized + ToOwned> Deref for Cow<'_, B> {
    type Target = B;

    fn deref(&self) -> &B {
        match *self {
            Borrowed(borrowed) => borrowed,
            Owned(ref owned) => owned.borrow(),
        }
    }
}
}

这种根据 enum 的不同状态来进行统一分发的方法是第三种分发手段, 可以使用泛型参数做静态分发和使用 trait object 做动态分发.

4. MutexGuard<T>

MutexGuard<T> 是一类很有意思的智能指针: 它不但通过 Deref 提供良好的用户体验, 还通过 Drop trait 来确保, 使用到的内存以外的资源在退出时进行释放.

MutexGuard这个结构是在调用 Mutex::lock 时生成的:

#![allow(unused)]
fn main() {
pub fn lock(&self) -> LockResult<MutexGuard<'_, T>> {
    unsafe {
        self.inner.raw_lock();
        MutexGuard::new(self)
    }
}
}

MutexGuard定义以及它的 DerefDrop 的实现,

#![allow(unused)]

fn main() {
// 这里用 must_use, 当你得到了却不使用 MutexGuard 时会报警
#[must_use = "if unused the Mutex will immediately unlock"]
pub struct MutexGuard<'a, T: ?Sized + 'a> {
    lock: &'a Mutex<T>,
    poison: poison::Guard,
}

impl<T: ?Sized> Deref for MutexGuard<'_, T> {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe { &*self.lock.data.get() }
    }
}

impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *self.lock.data.get() }
    }
}

impl<T: ?Sized> Drop for MutexGuard<'_, T> {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            self.lock.poison.done(&self.poison);
            self.lock.inner.raw_unlock();
        }
    }
}
}

MuxteGuard 不允许Send, 只允许Sync. 就是说, 你可以把 MutexGuard 的引用传给另一个线程使用, 但你无法把 MutexGuard 整个 move 到另一个线程.