mem模块函数库
mem::zeroed<T>() -> T
代码如下:
pub unsafe fn zeroed<T>() -> T { | |
// 调用者必须确认T类型的变量可以取全零值 | |
unsafe { | |
intrinsics::assert_zero_valid::<T>(); | |
MaybeUninit::zeroed().assume_init() | |
} | |
} |
mem::uninitialized<T>() -> T
用MaybeUnint::uninit获取一块未初始化内存,然后调用assume_init(), 此时内存彻底未初始化。
pub unsafe fn uninitialized<T>() -> T { | |
// 调用者必须确认T类型的变量允许未初始化的任意值 | |
unsafe { | |
intrinsics::assert_uninit_valid::<T>(); | |
MaybeUninit::uninit().assume_init() | |
} | |
} |
mem::take<T: Default>(dest: &mut T) -> T
将dest设置为默认内容(不改变所有权),用一个新变量返回dest的内容。
//使用take是一种照顾所有权的方式,直接用read会导致出现两份所有权,所以必须用replace清除原变量的所有权
pub fn take<T: Default>(dest: &mut T) -> T { | |
//即mem::replace,见下文 | |
replace(dest, T::default()) | |
} |
mem::replace<T>(dest: &mut T, src: T) -> T
用src的内容赋值dest(不改变所有权),用一个新变量返回dest的内容。
//因为所有权的关系,RUST一般不使用ptr::write来直接赋值,而用mem::replace完成对所有权处理的内存赋值。
pub const fn replace<T>(dest: &mut T, src: T) -> T { | |
unsafe { | |
//因为要替换dest, 所以必须对dest原有变量的所有权做处理,因此先用read将*dest的所有权转移到T,交由调用者进行处理 | |
//注意,此时*dest可能是MaybeUninit或ManuallyDrop的对象,如调用者不清楚类型,那必须显性调用drop函数 | |
let result = ptr::read(dest); | |
//ptr::write本身会导致src的所有权转移到dest,然后src被forget,这就处理了read()遗留的所有权问题 | |
ptr::write(dest, src); | |
result | |
} | |
} |
mem::transmute_copy<T, U>(src: &T) -> U
新建类型U的变量,并把src的内容拷贝到U。调用者应保证T类型的内容与U一致
pub const unsafe fn transmute_copy<T, U>(src: &T) -> U { | |
if align_of::<U>() > align_of::<T>() { | |
// 如果两个类型字节对齐U 大于 T. 使用read_unaligned | |
unsafe { ptr::read_unaligned(src as *const T as *const U) } | |
} else { | |
//用read即可完成 | |
unsafe { ptr::read(src as *const T as *const U) } | |
} | |
} |
mem::forget<T>(t:T)
通知RUST不做变量的drop操作
pub const fn forget<T>(t: T) { | |
//没有使用intrinsic::forget, 实际上效果一致,这里应该是尽量规避用intrinsic函数 | |
let _ = ManuallyDrop::new(t); | |
} |
mem::forget_unsized<T:Sized?>
对intrinsics::forget的封装
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
基本就是直接调用intrinsic模块的同名函数
mem::drop<T>(_x:T)
释放内存
ptr模块再探
ptr::read<T>(src: *const T) -> T
此函数用已有的类型复制出一个新的类型实体,对于不支持Copy Trait的类型,read函数是RUST实现未知类型变量的复制的一种方法,此函数作为内存函数take(), replace(), transmute_copy()的基础,底层使用intrisic::copy_no_overlapping支持,代码已经在MaybeUninit::assume_init_read
那里已经分析过
ptr::read_unaligned<T>(src: *const T) -> T
当数据结构中有未内存对齐的成员变量时,需要用此函数读取内容并转化为内存对齐的变量。否则会引发UB(undefined behaiver) 如下例:
/// 从字节数组中读一个usize的值:
use std::mem; | |
fn read_usize(x: &[u8]) -> usize { | |
assert!(x.len() >= mem::size_of::<usize>()); | |
let ptr = x.as_ptr() as *const usize; | |
//此处必须用ptr::read_unaligned,因为不确定字节是否对齐 | |
unsafe { ptr.read_unaligned() } | |
} |
例子中,为了从byte串中读取一个usize,需要用read_unaligned来获取值,不能象C语言那样通过指针类型转换直接获取值。
ptr::write<T>(dst: *mut T, src: T)
代码如下:
pub const unsafe fn write<T>(dst: *mut T, src: T) { | |
unsafe { | |
//浅拷贝 | |
copy_nonoverlapping(&src as *const T, dst, 1); | |
//必须调用forget,这里所有权已经转移。不允许再对src做drop操作 | |
intrinsics::forget(src); | |
} | |
} |
write函数本质上就是一个所有权转移的操作。完成src到dst的浅拷贝,然后调用了forget(src), 这使得src的Drop不再被调用(也规避src类型如果有引用导致的重复释放问题)。从而将所有权转移到dst。此函数是mem::replace, mem::transmute_copy的基础。底层由intrisic:: copy_no_overlapping支持。
这个函数中,如果dst已经初始化过,那原dst变量的所有权将被丢失掉,有可能引发内存泄漏。
ptr::write_unaligned<T>(dst: *mut T, src: T)
与read_unaligned相对应。举例如下:
struct Packed { | |
_padding: u8, | |
unaligned: u32, | |
} | |
let mut packed: Packed = unsafe { std::mem::zeroed() }; | |
// Take the address of a 32-bit integer which is not aligned. | |
// In contrast to `&packed.unaligned as *mut _`, this has no undefined behavior. | |
// 对于结构中字节没有按照2幂次对齐的成员,要用addr_of_mut!宏来获得地址,无法用取引用的方式。 | |
let unaligned = std::ptr::addr_of_mut!(packed.unaligned); | |
unsafe { std::ptr::write_unaligned(unaligned, 42) }; | |
assert_eq!({packed.unaligned}, 42); // `{...}` forces copying the field instead of creating a reference. |
ptr::read_volatile<T>(src: *const T) -> T
是intrinsics::volatile_load的封装
ptr::write_volatile<T>(dst: *mut T, src:T)
是intrinsics::volatiel_store的封装
ptr::macro addr_of($place:expr)
因为用&获得引用必须是字节按照2的幂次对齐的地址,所以用这个宏获取非地址对齐的变量地址
pub macro addr_of($place:expr) { | |
//关键字是&raw const,这个是RUST的原始引用语义,但目前还没有在官方做公开。 | |
//区别与&, &要求地址必须满足字节对齐和初始化,&raw 则没有这个问题 | |
&raw const $place | |
} |
ptr::macro addr_of_mut($place:expr)
作用同上。
pub macro addr_of_mut($place:expr) { | |
&raw mut $place | |
} |
指针的通用函数请参考Rust库函数参考
NonNull 与MaybeUninit相关函数
NonNull<T>::as_uninit_ref<`a>(&self) -> &`a MaybeUninit<T>
NonNull与MaybeUninit的引用基本就是直接转换的关系,一体双面
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() } | |
} |
NonNull<T>::as_uninit_mut<`a>(&self) -> &`a mut MaybeUninit<T>
NonNull<[T]>::as_uninit_slice<'a>(&self) -> &'a [MaybeUninit<T>]
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_slice_mut<'a>(&self) -> &'a mut [MaybeUninit<T>]
Unique
Unique类型结构定义如下
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>, | |
} |
和NonNull对比,Unique多了PhantomData类型变量。这个定义使得编译器知晓,Unique拥有了pointer指向的内存的所有权,NonNull没有这个特性。具备所有权后,Unique可以实现Send, Sync等Trait。因为获得了所有权,此块内存无法用于他处,这也是Unique的名字由来原因.
指针在被Unique封装前,必须保证是NonNull的
RUST用Allocator申请出来的内存的所有权用Unique做了绑定,使得内存进入了RUST的所有权和借用系统。
Unique模块的函数及代码与NonNull函数代码相类似,此处不分析。
Unique::cast<U>(self)->Unique<U>
类型转换,程序员应该保证T和U的内存布局相同
Unique::<T>::new(* mut T)->Option<Self>
此函数内部判断* mut T是否为0值
Unique::<T>::new_unchecked(* mut T)->Self
封装* mut T, 调用代码应该保证* mut T的安全性
Unique::as_ptr(self)->* mut T
Unique::as_ref(&self)->& T
因为Unique具备所有权,此处&T的生命周期与self相同,不必特别声明声明周期
Unique::as_mut(&mut self)->& mut T
同上