内存操作
裸指针
使用说明
裸指针代表一个内存块,指示了内存首地址、大小、对齐等属性及元数据。裸指针不能保证其代表的内存块是安全和有效的
- 将usize类型字面量强制转换成某类型的裸指针变量,使得以此字面量为首地址的内存块被转换为相应类型的变量
- 在不同类型的裸指针变量之间进行强制转换,实质上实现了裸指针所指变量的类型被强制转换
- 申请堆内存操作可以返回u8类型裸指针*const u8
- 对内存块按字节设置值,如清零或设置一个魔术值
- 对某些情况下,内存块拷贝是必需的高性能方案
- 利用裸指针偏移计算可以获取新的内存块,如数组访问、文件缓存等操作
- 当调用C语言中的函数时,需要将裸指针作为函数参数
一、裸指针的具体实现
*const T、 *mut T类型的结构体由两部分组成,第一部分是内存块首地址,第二部分是对这个内存块首地址的约束性描述 - 元数据
编译器对每一个类型的裸指针都实现了Pointee Trait元数据的获取。
复合类型的裸指针的元数据与它最后一个成员类型的裸指针的元数据相同,在这种情况下,可以对复合类型最后一个成员的裸指针,仅修改地址成员而得到复合类型的裸指针
// 裸指针的本质就是PtrComponents<T>
pub(crate) union PtrRepr<T: ?Sized> {
pub(crate) const_ptr: *const T,
pub(crate) mut_ptr: *mut T,
pub(crate) components: PtrComponents<T>,
}
pub(create) struct PtrComponents<T: ?Sized> {
// *const()保证data_address部分是一个数值
pub(crate) data_address: *const(),
// 元数据都需要实现Pointee Trait
pub(crate) metadata: <T as Pointee>::Metadata,
}
// Trait可以只用来定义关联类型
pub trait Pointee {
type Metadata: Copy + Send + Sync + Ord + Hash + Unpin;
}
// 瘦指针类型用于实现Thin Trait,即元数据类型为空的Pointee Trait
pub trait Thin = Pointee<Metadata = ()>;
元数据的规则
- 对于内存大小固定类型(实现了Sized Trait)的裸指针称为瘦指针(Thin Pointer)。它的元数据类型是空元组(),即没有元数据
- 对于动态大小类型(dst类型)的裸指针称为胖指针(Fat Pointer或Wide Pointer)。
- 对于复合类型,如果最后一个成员是dst类型,则元数据为此dst类型的元数据
- 对于str类型,元数据类型是usize,元数据值是按字节计算的长度值
- 对于[T]切片类型,元数据类型是usize,元数据值是切片成员的数据
- 对于Trait对象,如dyn SomtTrait,元数据是[DynMetadata]
二、固有模块裸指针关联函数
固有模块通常只有函数定义而没有函数实现,这些函数由编译器实现。
固有模块定义的裸指针关联函数通常仅由标准库的内存(mem)模块和指针(ptr)模块调用。
-
定义了释放资源的相关函数
-
intrainsics::forget<T: ?Sized?> (_:T)
此函数被调用后,编译器在变量的生命周期终止时,会“遗忘”对变量的drop()函数的自动调用
-
intrainsics::drop_in_place<T: ?Sized?> (to_drop: *mut T)
此函数被调用后, 会触发编译器调用指针所指变量的drop()函数,对同一裸指针调用此函数超过两次,会导致未定义错误
-
intrainsics::needs_drop<T>() -> bool
此函数用于判断T类型变量在生命周期终止时,是否需要调用drop()函数,实现Copy Trait的类型会返回false
-
-
定义了类型转换的相关函数
-
intrainsics::transmute<T, U>(e:T) -> U
此函数用于将T类型转换成U类型,T类型的内存布局与U类型的内存布局应该相同
-
-
定义了指针偏移的相关函数
-
intrainsics::offset<T>(dst: *const T, offset: usize) -> *const T
此函数用于返回dst+sizeof(T) *offset
-
intrainsics::ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize
此函数用于返回(ptr-base)/sizeof(T)
-
-
定义了内存拷贝的相关函数
-
intrainsics::copy<T>(src:*const T, dst: *mut T, count: usize)
此函数用于实现内存拷贝,参数src和dst所指变量内存可重叠。调用者要保证参数src和dst不能为NULL,并且满足内存对齐要求
如果参数src和dst已经被初始化,则此函数被调用后,参数src指向的变量所有权会出现双份,而参数dst指向的变量所有权会消失
调用此函数的代码必须处理这种情况以保证内存安全。参数中的count代码sizeof(T)*count的内存大小
-
intrainsics::copy_no_overlapping<T>(src: *const T, dst: *mut T, count: usize)
此函数用于实现内存拷贝,参数src和dst所指变量内存应该保证不重叠,其他与上面函数完全相同
-
intrainsics::write_bytes(dst: *mut T, val: u9, count: usize)
此函数用于内存赋值,如果参数dst所指变量内存已经被初始化,则调用此函数可能导致参数dst指向的变量所有权消失
-
-
定义了获取内存属性的相关函数
-
intrainsics::size_of<T>() -> usize
此函数用于返回类型内存占用空间大小,单位为字节
-
intrainsics::min_align_of<T>() -> usize
此函数用于返回类型内存对齐大小,单位为字节
-
intrainsics::size_of_val<T>(_:*count T) -> usize
此函数用裸指针变量获取类型内存占用空间,单位为字节
-
intrainsics::min_align_of_val<T>(_: *count T) -> usize
此函数用裸指针变量获取类型内存对齐大小,单位为字节
-
-
定义了不需要编译器对代码做内存优化的场景,通常对硬件寄存器操作的代码需要禁止优化
-
intrainsics::volatile_load<T>(src: *const T) -> T
此函数用于读取内存或寄存器,参数src需要满足T类型的内存对齐要求
-
intrainsics::volatile_store<T>(dst: *mut T, val: T)
此函数用于写入内存或寄存器,参数dst需要满足内存对齐要求
-
intrainsics::unaligned_volatile_load<T>(src: *const T) -> T
此函数的参数src无须满足内存对齐要求
-
intrainsics::unaligned_volatile_store<T>(dst: *mut T, val: T)
此函数的参数dst无须满足内存对齐要求
-
-
定义了获取内存块比较相关函数
-
intrainsics::raw_eq<T>(a: &T, b: &T) -> bool
此函数用于实现内存内容比较
-
pub fn ptr_guaranteed_eq<T>(ptr: *const T, other: *const T) -> bool
此函数用于判断两个指针是否相等
-
pub fn ptr_guaranteed_ne<T>(ptr: *const T, other: *const T) -> bool
此函数用于判断两个指针是否不相等
-
三、裸指针操作
-
将变量引用直接强制转换为裸指针变量
&T as *const T; &mut T as *mut T;
-
将usize的数值直接强制转换为裸指针变量
let a: usize = 0xf000000000000; unsafe { a as *const i32 }
-
标准库的ptr模块还提供了若干构造裸指针变量的函数
// 此函数用于构造0值的*const T ptr::null<T>() -> *const T ptr::null_mut<T>() -> *mut T // 此函数用于将一个数值转换为不可变裸指针,函数名称明示裸指针是无效的 ptr::invalid<T>(addr: usize) -> *const T // 此函数用于将一个数值转换为无效可变裸指针 ptr::invalid_mut<T>(addr: usize) -> *mut T // 此函数用内存地址和元数据构造不可变裸指针 ptr::invalid_raw_parts<T: ?Sized>(data_address: *const(), metadata: <T as Pointee>::Metadata) -> *const T // 此函数用内存地址和元数据构造可变裸指针 pub const fn from_raw_part<T: ?Sized> ( data_address: *const (), metadata: <T as Pointee>::Metadata, ) -> *const T { // *const T实质就是PtrRepr类型结构体 unsafe { PtrRepr { componnts: PtrComponents { data_address, metadata } }.const_ptr } } // 此函数用切片第一个成员裸指针及切片长度构造切片类型裸指针,当调用代码时应该保证内存安全 // 一个典型应用场景是程序申请堆内存:代码调用底层函数申请堆内存,底层函数返回内存块首地址*const u8及以字节计数的大小。随后调用slice_from_raw_parts()函数,用这两个值构造*const [u8],用一个裸指针变量包含内存块首地址及内存块大小信息 pub const fn slice_from_raw_parts<T>(data: *const T, len: usize) -> *const [T] { // data.cast()函数用于将*const T转换为*const()函数,再应用from_raw_parts()函数形成切片类型裸指针 from_raw_parts(data.cast(), len) }
-
实现内存块的类型转换
// 此函数等价于self as *const U, 可用于支持函数式编程的链式调用 *const T::cast<U>(self) -> *const U // *mut T::cast<U>(self) -> *mut U
程序调用cast()函数后,如果要对转换后的裸指针解引用,就必须保证T类型的内存布局与U类型的内存布局完全一致。如果仅将转换后的裸指针做数值应用,就没有此限制
-
用于将裸指针转换为引用函数
*const T::as_ref<'a>(self) -> Option(&'a T) *mut T::as_ref<'a>(self) -> Option(&'a T) *mut T::as_mut<'a>(self) -> Option(&'a mut T)
-
切片类型的裸指针获取切片第一个成员的裸指针的函数
ptr::*const [T]::as_ptr(self) -> *const T ptr::*mut [T]::as_mut_ptr(self) -> *mut T
-
获取裸指针内部属性的函数
// 此函数用于获取不可变裸指针的首地址及元数据 ptr::*const T::to_raw_parts(self) -> (*const (), <T as super::Pointee>::Metadata) ptr::*mut T::to_raw_parts(self) -> (*const (), <T as super::Pointee::Metadata) // 此函数用于判断裸指针地址成员是否为0 ptr::*const T::is_null(self) -> bool ptr::*mut T::is_null(self) -> bool // 此函数用于获取切片类型长度 ptr::*const [T]:: len(self) -> usize ptr::*mut [T]:: len(self) -> usize
-
对当前裸指针的内存地址做偏移来获取序列中其他变量的裸指针
// 以下函数对intrinsic同名函数的封闭 // 此函数用于获取self偏移count个T类型内存大小后的裸指针 ptr::*const T::offset(self, count:isize)->*const T // 此函数用于获取用溢出回绕方式计算偏移后的裸指针 ptr::*const T::wrapping_offset(self, count: isize) -> *const T // 此函数用于计算两个裸指针间有多少个T类型内存大小 ptr::*const T::offset_from(self, origin: *const T) -> isize // 此函数用于获取self偏移count后的可变裸指针 ptr::*mut T::offset(self, count:isize) -> *mut T // 此函数用于获取用溢出回绕方式计算偏移后的裸指针 ptr::*mut T::wrapping_offset(self, count: isize) -> *mut T // 此函数用于计算两个裸指针间有多少个T类型内存大小 ptr::*mut T::offset_from(self, origin: *mut T) -> isize // 更加直观的函数名 ptr::*const T::add(self, count: usize) -> Self ptr::*const T::wraping_add(self, count: usize) -> Self ptr::*const T::sub(self, count:usize) -> Self ptr::*const T::wrapping_sub(self, count:usize) -> Self ptr::*mut T::add(self, count: usize) -> Self ptr::*mut T::wraping_add(self, count: usize) -> Self ptr::*mut T::sub(self, count:usize) -> Self ptr::*mut T::wrapping_sub(self, count:usize) -> Self
-
其他裸指针函数
// 此函数是对intrinsic模块同名函数的封装 ptr::drop_in_place<T: ?Sized>(to_drop: *mut T) // 此函数用于返回裸指针的元数据,如切片裸指针可以利用此函数获取切片长度 ptr::metadata<T: ?Sized>(ptr: *const T) -> <T as Pointee>::Metadata // 此函数用于比较两个裸指针,即比较内存地址,又比较元数据 ptr::eq<T>(a: *const T, b: *const T) -> bool
四、裸指针再论
标准库提供了内存对齐类型的读取裸指针函数及内存不对齐类型的读取裸指针函数,而读取裸指针函数本质是内存拷贝
// ptr::read()函数是对所有类型通用的复制方法之一,此函数仅用于完成浅拷贝,并且会转移所有权
ptr::read<T>(src: *const T) -> T
// 此函数是参数src不满足内存对齐时的内存拷贝函数,返回值是内存对齐的变量
ptr::read_unaligned<T>(src: *const T) -> T
// tr::read_unaligned()函数举例如下:从字节数组中读取一个函数的使用
fn read_usize(x: &[u8]) -> usize {
let ptr = x.as_ref() as *const usize;
// 此处必须使用ptr.read_unaligned()函数,因为不确定节点是否对齐
unsafe { ptr.read_unaligned() }
}
标准库提供了内存对齐类型的写入裸指针函数及内存不对齐类型的写入裸指针函数,而写入裸指针函数本质是内存拷贝,但增加了所有权处理
pub const unsafe fn write<T>(dst: *mut T, src: T) {
unsafe {
// 完成内存浅拷贝
copy_nonoverlapping(&src as *const T, dst, 1);
// 必须调用forget()函数,所有权已经转移到dst所指变量,通过编译器无须再调用src的drop()函数
intrinsics::forget(src);
}
}
// 与ptr.read_unaligned()函数相对应的写操作
ptr::write_unaligned<T>(dst: *mut T, src: T)
在某些不允许编译器优化的场景中,如读/写硬件寄存器地址,也属于读取及写入裸指针的操作。需要使用禁止优化的读取或写入裸指针函数
// 实质是intrinsics::volatile_load()函数的封装
ptr::read_volatile<T>(src: *const T) -> T
// 实质是intrinsics::volatile_store()函数的封装
ptr::write_volatile<T>(dst: *mut T, src: T)
MaybeUninit - 未初始化变量方案
应用场景
- 申请的堆内存块,这是未初始化的内存
- 需要定义一个新的泛型变量,并且不合适用转移所有权的“=”进行初始化赋值
- 需要定义一个新的变量,但希望初始化之前就能使用其引用
- 定义一个数组,但无法在定义是对数组成员初始化
MaybeUninit定义
#[repr(transparent)]
pub union MaybeUninit<T> {
// 引成员用于未初始化变量时赋值,以满足编译器的要求
uninit: (),
// 确定内存大小
// 未初始化时保证编译器不会调用变量的drop()函数
// 提供内存写入函数以及初始化内存
value: ManuallyDrop<T>,
}
属性repr(transparent)表示复合类型变量在内存中等价于内部成员变量,通常用于union类型或仅有一个成员的封装类型
ManuallyDrop的定义
[repr(transparent)]
pub struct ManuallyDrop<T: ?Sized> {
// 当ManuallyDrop变量的生命周期终止时,不会触发编译器调用value的drop()函数以释放资源
value: T,
}
impl<T> ManuallyDrop<T> {
// 用于构造ManuallyDrop变量,当调用new()函数时,参数value已经被初始化
pub const fn new(value: T) -> ManuallyDrop<T> {
// 将value所有权转移到结构体之后,编译器不会在value的生命周期终止时调用drop()函数
ManuallyDrop { value }
}
// 此关联函数消费了ManuallyDrop<T>,并将内部变量及所有权返回
pub const fn into_inner(slot: ManuallyDrop<T>) -> T {
slot.value
}
// 关联函数drop()会触发调用内部变量的drop()函数来释放资源
pub drop(slot: &mut ManuallyDrop<T>)
// 关联函数take()在不封装的情况下,用于获取内部变量的所有权
pub unsafe fn take(slot: &mut ManuallyDrop<T>) -> T {
// 复制内部变量,连同所有权一起返回,后继不能再调用into_inner()函数,否则会出现双份所有权,导致UB(未定义行为)
unsafe { ptr::read(&slot.value) }
}
}
MaybeUninit方法
impl<T> MaybeUninit<T> {
// 一种申请栈内存的方案,内存块的大小是T类型的内存空间大小。
// 经常用于需要在栈空间定义一个未初始化变量。
pub const fn uninit() -> MaybeUninit<T> {
MaybeUninit { uninit: () }
}
pub const fn new(val: T) -> MaybeUninit<T> {
// 因为val变量已经初始化,所以后继要保证val变量被正确释放
MaybeUninit { value: ManuallyDrop::new(val) }
}
// 关联函数zeroed()用于创建内存块清零的MaybeUninit<T> 变量
pub fn zeroed() -> MaybeUninit<T> {
let mut u = MaybeUninit::<T>::uninit();
// 给内存块清零必须使用ptr::write_bytes
unsafe { u.as_mut_ptr().write_bytes(0u8, 1); }
u
}
// 此函数返回的&mut T的生命周期短于self的生命周期
pub const fn write(&mut self, val: T) -> &mut T {
// 以下代码的赋值操作会使*self原有MybeUninit<T>变量的生命周期终止。但不会调用内部变量的drop()函数
// 所以下面代码中的*self内部变量必须是未初始化的,或者T类型变量不需要调用drop()函数释放占用资源。否则会造成内存泄漏
*self = MaybeUninit::new(val);
// 初始化后可以调用assume_init_mut()函数返回可变引用
unsafe { self.assume_init_mut() }
}
// 使用write()函数完成对内部变量赋值之后,还需要调用解封装函数,从而完成全部的初始化过程。
// 如果没有调用解封装函数,可以会导致资源泄漏
// 调用代码必须保证self已经被初始化
pub const unsafe fn assume_init(self) -> T {
unsafe {
intrinsics::assert_inhabited::<T>();
// 消费self及self.value,返回内部变量及所有权
ManuallyDrop::into_inner(self.value)
}
}
// 在不消费self的情况下,用于获取内部变量的所有权
// 调用assume_init_read()之后,不能再对self调用其他assume_init_xxx()函数,否则会出现双份所有权
pub const unsafe fn assume_init_read(&self) -> T {
unsafe {
intrinsics::assert_inhabited::<T>();
// 跟踪read,最后调用ptr::read()函数
self.as_ptr().read()
}
}
// prt::read()函数用于完成变量的复制。调用此函数后,参数src所指变量的所有权已经转移到函数返回
// 调用此函数的前提是,后续代码保证参数src所指变量不用调用drop()函数
pub const unsafe fn read<T>(src: *const T) -> T {
let mut tmp = MaybeUninit::<T>::uninit();
unsafe {
copy_nonoverlapping(src, tmp.as_mut_ptr(), 1);
tmp.assume_init()
}
}
// 此函数用于申请数组的栈空间内存
pub const fn uninit_array<const LEN: usize>() -> [Self; LEN] {
unsafe { MaybeUninit::<[MaybeUninit<T>; LEN]>::uninit().assume_init() }
}
// 此函数性能低,最好不使用此函数,可以使用数组引用实现对数组的访问
pub unsafe fn array_assume_init<const N: usize>(array: [Self; N]) -> [T; N] {
unsafe { (&array as *const _ as *const [T; N]).read() }
}
}
其他assume_init_xxx()函数
// 此函数对已经初始化的内部变量调用其drop()函数
MaybeUninit<T>::assume_init_drop(&self)
// 此函数用于返回内部变量的引用,调用者应该保证内部变量已经初始化
MaybeUninit<T>::assume_init_ref(&self) -> &T
// 此函数用于返回内部变量的可变借用
MaybeUninit<T>::assume_init_mut(&mut self) -> &mut T
NonNull - 非空裸指针
NonNull类型保证裸指针地址成员的取值不能为0,可使用Option<NonNull>来表示整个裸指针集合,其中None来表示空指针
#[repr(transparent)]
pub struct NonNull<T: ?Sized> {
// pointer为私有变量,只能通过函数设置,为保证pointer不为空奠定了基础
pointer: *const T,
}
构造关联函数
// 关联函数dangling()用于创建一个悬垂(dangling)指针,并保证悬垂指针满足类型内存对齐要求且非零。
// 此函数返回的NonNull<T>指针严禁被解引用
impl<T> NonNull<T> {
pub const fn dangling() -> Self {
unsafe {
// 取内存对齐地址作为裸指针的地址,调用代码应保证不对此内存地址进行读写
let ptr = mem::align_of::<T>() as *mut T;
NonNull::new_unchecked(ptr)
}
}
}
// NonNull内部封装的是可变指针
pub fn new(ptr: *mut T) -> Option<Self> {
// 完成对ptr是否为0的检查
if !ptr.is_null() {
// new_unchecked()函数用于检查*mut T是否为0
Some(unsafe { Self::new_unchecked(ptr) })
} else {
None
}
}
// 关联函数from_raw_parts()是裸指针模块同名函数的NonNull<T>版本
pub const fn from_raw_parts(
data_address: NonNull<()>,
metadata: <T as super::Pointee>::Metadata,
) -> NonNull<T> {
unsafe {
// 需要用from_raw_parts_mut()函数创建*mut T指针
NonNull::new_unchecked(super::from_raw_parts_mut(data_address.as_ptr(), metadata))
}
}
// From Trait可以利用引用变量创建NonNull<T>
impl<T: ?Sized> const From<&mut T> for NonNull<T> {
fn from(reference: &mut T) -> Self {
unsafe { NonNull { pointer: reference as *mut T } }
}
}
impl<T: ?Sized> const From<&T> for NonNull<T> {
fn from(reference: &T) -> Self {
// NonNull也可以接收不可变引用,后继代码不能将这个变量转换为可变引用
unsafe { NonNull { pointer: reference as *const T } }
}
}
类型转换函数
// 此函数用于返回内部的pointer裸指针
NonNull::<T>::as_ptr(self) -> *mut T
// 此函数用于返回引用,其生命由调用代码决定,调用代码要保证生命周期的安全
NonNull::<T>::as_ref<'a>(&self) -> &'a T
// 此函数用于返回可变引用。无论在创建NonNull时,是否传入可变指针
NonNull::<T>::as_mut<'a>(&mut self) ->'a mut T
// 此函数用于完成指针类型转换,调用代码应该保证T类型的内存布局和U类型的内存布局相同
NonNull::<T>::cast<U>(self) -> NonNull<U>
其他函数
// 此函数用于将指针转化为切片类型指针
NonNull::<[T]>::slice_from_raw_parts(data: NonNull<T>, len: usize) -> Self
// 此函数用于将切片指针转换为切片第一个成员的指针
NonNull::<[T]>::as_non_null_ptr(self) -> NonNull<T>
// 将NonNull<T>与MaybeUninit<T>进行转换
pub unsafe fn as_uninit_ref<'a>(&self) -> &'a MaybeUninit<T> {
// self.cast()函数用于将NonNull<T>转换为NonNull<MaybeUninit<T>>
// self.cast().as_ptr()函数用于将NonNull<MaybeUninit<T>>转换为*mut MaybeUninit<T>
unsafe { &*self.cast().as_ptr() }
}
pub unsafe fn as_uninit_slice<'a>(&self) -> &'a [MaybeUninit<T>] {
// 最终导致对ptr::slice_from_raw_parts()函数的调用
unsafe { slice::from_raw_parts(self.cast().as_ptr(), self.len() ) }
}
// 可变引用的版本
NonNull<T>::as_uninit_mut<'a>(&self) -> &'a mut MaybeUninit<T>
NonNull<[T]>::as_uninit_slice_mut<'a>(&self) -> &'a mut [MaybeUninit<T>]
Unique - 智能指针的基座
Unique通常作为智能指针类型的成员变量。智能指针用Unique指向申请的堆内存块及拥有此内存块的所有权。当智能指针类型的生命周期终止时,会释放Unique变量所指的内存资源
#[repr(transpartent)]
pub struct Unique<T: ?Sized> {
// 保证不为空值
pointer: *const T,
// 拥有了pointer指向的内存变量的所有权
_marker: PhantomData<T>,
}
Unique变量拥有pointer所指变量的所有权,也使Unique成员具有生命周期的类型。因此,Unique可以支持Send、Sync等Trait.
// 此关联函数用于创建Unique<T>变量
Unique::<T>::new(*mut T) -> Option<Self>
// 此函数用于将Unique<T>变量转换为裸指针
Unique::as_ptr(self) -> *mut T
// 此函数用于将Unique<T>变量转换为引用,返回&T的生命周期应短于self的生命周期
Unique::as_ref(&self) -> &T
// 此函数用于将Unique<T>变量转换为可变引用
Unique::as_mut(&mut self) -> &mut T
// 此函数用于实现类型转换,调用代码应该保证T类型的内存布局和U类型的内存布局相同
Unique::cast<U>(self) -> Unique<U>
men模块函数
构造泛型变量函数
men::zero()函数用于在栈空间创建一个内存块清零的泛型变量。
// 调用此函数必须确认T类型变量内存块可以清零,否则代码在返回变量的生命周期终止时会出现未定义行为
pub unsafe fn zeroed<T>() -> T {
unsafe {
inirinsics::assert_zero_valid::<T>();
MaybeUninit::zeroed().assume_init()
}
}
mem::uninitialized()函数用于在栈空间创建一个未初始化的泛型变量。
// 调用此函数必须确认T类型变量内存允许为任意值,否则代码在返回变量的生命周期终止时,会出现未定义的行为
pub unsafe fn uninitialized<T>() -> T {
unsafe {
intrinsics::assert_uninit_valid::<T>();
MaybeUninit::uninit().assume_init()
}
}
泛型变量所有权转移函数
在仅拥有可变引用的场景下,将可变引用所指变量的所有权转移到一个新的变量。因为可变引用不支持*&mut T
表达式,所有需要使用内存拷贝的方式实现
// 此函数使用泛型变量的默认值赋值dest,并返回原dest的内容及所有权
pub fn take<T: Default>(dest: &mut T) -> T {
// 此处调用replace()函数对所有权进行处理
replace(dest, T::deafult())
}
// 此函数使用src的内容赋值dest(将src的所有权转移到dest),并返回原dest的内容及所有权
pub const fn <T>(dest: &mut T, src: T) -> T {
// 因为要替换dest,所有必须对dest原有变量的所有权进行处理,利用ptr::read()函数将*dest的所有权转移到返回变量
// 如果T类型不支持Copy Trait,则= *&t无法通过编译
// 掺权进行转移的方式只有内存浅拷贝这种方法
let result = ptr::read(dest);
ptr::write(dest, src);
result
}
内存操作示意图
mem::transmute_copy()函数用于创建U类型的变量,并把src的内容复制给U类型的变量。
调用代码应该保证T类型的内存布局与U类型的内存布局不冲突,并处理好src的所有权
pub const unsafe fn transmute_copy<T, U>(src: &T) -> U {
// 如果U类型字节对齐字节数量大于T类型字节对齐数量
if align_of::<U>() > align_of::<T>() {
// 使用ptr::read_unaligned()函数
unsafe { ptr::read_unaligned(src as *const T as *const U) }
} else {
// 使用ptr::read()函数
unsafe { ptr::read(src as *const T as *const U) }
}
}
其他函数
mem模块包括一些固有模块函数的封装函数
pub const fn forget<T>(t: T) {
let _ = ManuallyDrop::new(t);
}
// 此函数内部是intrinsics::forget()函数
mem::forget_unsized<T: Sized?>
mem::drop<T>(_x: T)
mem::size_of<T> -> usize
mem::min_align_of<T>) -> usize
mem::size_of_val<T>(val: &T) -> usize
mem::min_align_of_val<T>(val: &T) -> usize
mem::needs_drop<T>() -> bool