本書(shū)github鏈接:inside-rust-std-library
前面章節(jié)參見(jiàn):
深入RUST標(biāo)準(zhǔn)庫(kù)內(nèi)核(序言) - 簡(jiǎn)書(shū) (jianshu.com)
深入RUST標(biāo)準(zhǔn)庫(kù)內(nèi)核(一 概述) - 簡(jiǎn)書(shū) (jianshu.com)
深入RUST標(biāo)準(zhǔn)庫(kù)內(nèi)核(二 內(nèi)存)—Layout/原生指針 - 簡(jiǎn)書(shū) (jianshu.com)
深入RUST標(biāo)準(zhǔn)庫(kù)內(nèi)核(二 內(nèi)存)—NonNull<T>/申請(qǐng)及釋放 - 簡(jiǎn)書(shū) (jianshu.com)
mem模塊結(jié)構(gòu)及函數(shù)
MaybeUninit<T>
MaybeUninit<T>結(jié)構(gòu)定義
源代碼如下:
#[repr(transparent)]
pub union MaybeUninit<T> {
uninit: (),
value: ManuallyDrop<T>,
}
MaybeUninit的內(nèi)存布局就是ManuallyDrop<T>
的內(nèi)存布局麦撵,從后文可以看到莺琳,ManuallyDrop<T>
實(shí)際就是T的內(nèi)存布局峻呛。所以MaybeUninit在內(nèi)存中實(shí)質(zhì)也就是T類(lèi)型揉燃。
RUST的引用使用的內(nèi)存塊必須保證是內(nèi)存對(duì)齊及賦以初始值蒸辆,未初始化的內(nèi)存塊和清零的內(nèi)存塊都不能滿足引用的條件跟磨。但堆內(nèi)存申請(qǐng)后都是未初始化的定罢,且在程序中某些情況下也需要先將內(nèi)存設(shè)置為未初始化糠溜,尤其在處理泛型時(shí)淳玩。因此,RUST提供了MaybeUninit<T>容器來(lái)實(shí)現(xiàn)對(duì)未初始化變量的封裝非竿,從而可以不引發(fā)編譯錯(cuò)誤完成某些對(duì)T類(lèi)型未初始化變量的操作.
MaybeUninit<T>利用ManuallyDrop<T>的方式對(duì)T的未初始化進(jìn)行了一個(gè)標(biāo)識(shí)蜕着。這對(duì)T也有一個(gè)保護(hù),使得未初始化的變量免于被RUST自動(dòng)調(diào)用drop所釋放掉.
ManuallyDrop<T> 結(jié)構(gòu)及行為
源代碼如下:
#[repr(transparent)]
pub struct ManuallyDrop<T: ?Sized> {
value: T,
}
一個(gè)變量被ManuallyDrop獲取所有權(quán)后红柱,RUST編譯器將不再對(duì)其自動(dòng)調(diào)用drop操作承匣。需要代碼顯式的調(diào)用drop來(lái)釋放置入ManuallyDrop的T類(lèi)型變量。
ManuallyDrop主要使用場(chǎng)景:
- 作為MaybeUninit的內(nèi)部結(jié)構(gòu)锤悄,對(duì)未初始化的內(nèi)存做一個(gè)保護(hù)和標(biāo)識(shí)韧骗。
- 希望由代碼顯式釋放變量時(shí)。
重點(diǎn)關(guān)注的一些行為:
ManuallyDrop<T>::new(val:T) -> ManuallyDrop<T>
, 此函數(shù)返回ManuallyDrop變量擁有傳入的T類(lèi)型變量所有權(quán)零聚,并將此塊內(nèi)存直接用ManuallyDrop封裝, 對(duì)于val袍暴,編譯器不再主動(dòng)做drop操作。這里隶症,T實(shí)際上應(yīng)該是實(shí)現(xiàn)了Sized的類(lèi)型
ManuallyDrop<T>::into_inner(slot: ManuallyDrop<T>)->T
, 將封裝的T類(lèi)型變量所有權(quán)轉(zhuǎn)移出來(lái)政模,編譯器會(huì)重新將返回的變量納入drop管理體系。
ManuallyDrop<T>::take(slot: &mut ManuallyDrop<T>)->T
沿腰,實(shí)質(zhì)是復(fù)制一個(gè)變量览徒。 不影響原有的變量。
ManuallyDrop<T>::drop(slot: &mut ManuallyDrop<T>)
颂龙,手動(dòng)drop掉內(nèi)部變量习蓬。
ManuallyDrop<T>::deref(&self)-> & T
, 返回內(nèi)部包裝的變量的引用纽什,返回的引用可正常使用
ManuallyDrop<T>::deref_mut(&mut self)-> & mut T
返回內(nèi)部包裝的變量的可變引用,返回的引用可正常使用
ManuallyDrop代碼舉例:
use std::mem::ManuallyDrop;
let mut x = ManuallyDrop::new(String::from("Hello World!"));
x.truncate(5); // 可以對(duì)x進(jìn)行操作
assert_eq!(*x, "Hello");
// 但對(duì)x的drop不會(huì)再發(fā)生
MaybeUninit<T> 行為
MaybeUninit<T>提供了在GlobalAlloc Trait之外的一種獲取內(nèi)存的方法躲叼, 實(shí)際上可類(lèi)比為泛型 new()的一種實(shí)現(xiàn)方式,不過(guò)返回的不是指針芦缰,而是變量。MaybeUninit<T>獲取的內(nèi)存位于椃憧叮空間让蕾。
MaybeUninit<T>::uninit()->MaybeUninit<T>
, 是MaybeUninit<T>棧上申請(qǐng)內(nèi)存的行為,申請(qǐng)的內(nèi)存大小是T類(lèi)型的內(nèi)存大小或听,該內(nèi)存沒(méi)有初始化探孝。利用泛型和Union內(nèi)存布局,RUST巧妙的實(shí)現(xiàn)了在棧上申請(qǐng)一塊未初始化內(nèi)存誉裆。此函數(shù)非常非常非常值得關(guān)注顿颅,是非常多場(chǎng)景下的代碼解決方案。
MaybeUninit<T>::new(val:T)->MaybeUninit<T>
, 內(nèi)部用ManuallyDrop封裝了val, 然后用MaybeUninit封裝ManuallyDrop足丢。因?yàn)槿绻鸗沒(méi)有初始化過(guò)粱腻,調(diào)用這個(gè)函數(shù)會(huì)編譯失敗,所以此時(shí)內(nèi)存實(shí)際上已經(jīng)初始化過(guò)了斩跌。
MaybeUninit<T>::zeroed()->MaybeUninit<T>
, 申請(qǐng)了T類(lèi)型內(nèi)存并清零绍些。
pub fn zeroed() -> MaybeUninit<T> {
let mut u = MaybeUninit::<T>::uninit();
// SAFETY: `u.as_mut_ptr()` points to allocated memory.
unsafe {
//必須使用write_bytes,否則無(wú)法給內(nèi)存清0
u.as_mut_ptr().write_bytes(0u8, 1);
}
u
}
MaybeUninit<T>::assume_init()->T
,代碼如下:
pub const unsafe fn assume_init(self) -> T {
// SAFETY: the caller must guarantee that `self` is initialized.
// This also means that `self` must be a `value` variant.
unsafe {
intrinsics::assert_inhabited::<T>();
//把T的所有權(quán)返回耀鸦,編譯器會(huì)主動(dòng)對(duì)T調(diào)用drop
ManuallyDrop::into_inner(self.value)
}
}
MaybeUninit<T>::assume_init_read()->T
此函數(shù)最后會(huì)調(diào)用ptr::read()函數(shù)柬批。代碼如下:
pub const unsafe fn assume_init_read(&self) -> T {
unsafe {
intrinsics::assert_inhabited::<T>();
//會(huì)調(diào)用ptr::read
self.as_ptr().read()
}
}
//此函數(shù)會(huì)復(fù)制一個(gè)變量,如果類(lèi)型T含有引用或智能指針揭糕,僅僅調(diào)用這個(gè)函數(shù)會(huì)可能導(dǎo)致內(nèi)存重復(fù)釋放問(wèn)題萝快,
//但在assume_init_read()中使用此函數(shù)不會(huì)導(dǎo)致問(wèn)題,因?yàn)閟rc被ManuallyDrop封裝著角,不會(huì)被釋放揪漩。
pub const unsafe fn read<T>(src: *const T) -> T {`
//利用MaybeUninit::uninit申請(qǐng)未初始化的T類(lèi)型內(nèi)存
let mut tmp = MaybeUninit::<T>::uninit();
//SAFETY: the caller must guarantee that `src` is valid for reads.
// `src` cannot overlap `tmp` because `tmp` was just allocated on
// the stack as a separate allocated object.
//
// Also, since we just wrote a valid value into `tmp`, it is guaranteed
// to be properly initialized.
unsafe {
//完成內(nèi)存拷貝
copy_nonoverlapping(src, tmp.as_mut_ptr(), 1);
//初始化后的內(nèi)存移出ManuallyDrop 并返回
tmp.assume_init()
}
}
可見(jiàn),assume_init_read 行為實(shí)際上是從一個(gè)已有類(lèi)型生成并復(fù)制一個(gè)新的變量吏口。此時(shí)原有的MaybeUninit變量可保持原狀奄容。
MaybeUninit<T>::assume_init_drop()
對(duì)內(nèi)部變量進(jìn)行drop操作
MaybeUninit<T>::assume_init_ref()->&T
返回內(nèi)部T類(lèi)型變量的借用,調(diào)用者應(yīng)保證內(nèi)部T類(lèi)型變量已經(jīng)初始化产徊,&T此時(shí)是完全正常的
MaybeUninit<T>::assume_init_mut()->&mut T
返回內(nèi)部T類(lèi)型變量的可變借用昂勒,調(diào)用者應(yīng)保證內(nèi)部T類(lèi)型變量已經(jīng)初始化,&mut T此時(shí)是完全正常的
MaybeUninit<T>::write(val)->&mut T
, 代碼如下:
pub const fn write(&mut self, val: T) -> &mut T {
//通常情況下舟铜,如果*self是初始化過(guò)得戈盈,那調(diào)用下面的等式時(shí),會(huì)立刻調(diào)用*self擁有所有權(quán)變量的drop。但因?yàn)镸aybeUninit<T>封裝的變量不會(huì)被drop塘娶。所以下面這個(gè)等式實(shí)際上隱含了 *self必須是未初始化的归斤,否則的話,這里會(huì)丟失掉已初始化的變量所有權(quán)信息刁岸,可能造成內(nèi)存泄漏脏里。
*self = MaybeUninit::new(val);
// SAFETY: We just initialized this value.
unsafe { self.assume_init_mut() }
}
MaybeUninit<T>::uninit_array<const LEN:usize>()->[Self; LEN]
此處對(duì)LEN的使用方式需要注意,這是不常見(jiàn)的一個(gè)泛型寫(xiě)法,這個(gè)函數(shù)同樣的申請(qǐng)了一塊內(nèi)存虹曙。代碼:
pub const fn uninit_array<const LEN: usize>() -> [Self; LEN] {
// SAFETY: An uninitialized `[MaybeUninit<_>; LEN]` is valid.
unsafe { MaybeUninit::<[MaybeUninit<T>; LEN]>::uninit().assume_init() }
}
這里要注意區(qū)別數(shù)組類(lèi)型和數(shù)組元素的初始化迫横。對(duì)于數(shù)組[MaybeUninit<T>;LEN]這一類(lèi)型本身來(lái)說(shuō),初始化就是確定整體的內(nèi)存大小酝碳,所以數(shù)組類(lèi)型在聲明后就已經(jīng)完成了矾踱。所以此時(shí)assume_init()是正確的。這是一個(gè)理解上的盲點(diǎn)击敌。
MaybeUninit<T>::array_assume_init<const N:usize>(array: [Self; N]) -> [T; N]
這個(gè)函數(shù)沒(méi)有把所有權(quán)轉(zhuǎn)移出來(lái)介返,代碼分析如下:
pub unsafe fn array_assume_init<const N: usize>(array: [Self; N]) -> [T; N] {
// SAFETY:
// * The caller guarantees that all elements of the array are initialized
// * `MaybeUninit<T>` and T are guaranteed to have the same layout
// * `MaybeUninit` does not drop, so there are no double-frees
// And thus the conversion is safe
unsafe {
//最后是調(diào)用是*const T::read(),此處 as *const _的寫(xiě)法可以簡(jiǎn)化代碼,這里沒(méi)有把T類(lèi)型變量所有權(quán)轉(zhuǎn)移到返回值
//返回后沃斤,此MaybeUninit變量應(yīng)該被丟棄
(&array as *const _ as *const [T; N]).read()
}
}
MaybeUninit<T>一些典型使用代碼例子:
Box的內(nèi)存申請(qǐng)例子:
pub fn try_new_uninit_in(alloc: A) -> Result<Box<mem::MaybeUninit<T>, A>, AllocError> {
let layout = Layout::new::<mem::MaybeUninit<T>>();
//allocate(layout)?返回NonNull<[u8]>, NonNull<[u8]>::<MaybeUninit<T>>::cast()返回NonNull<MaybeUninit<T>>
let ptr = alloc.allocate(layout)?.cast();
//以下代碼在Box結(jié)構(gòu)時(shí)分析
unsafe { Ok(Box::from_raw_in(ptr.as_ptr(), alloc)) }
}
以上代碼可以看到,NonNull<[u8]>可以直接通過(guò)cast 轉(zhuǎn)換為NonNull<MaybeUninit<T>>, 這是另一種MaybeUninit<T>的生成方法刃宵,直接通過(guò)指針類(lèi)型轉(zhuǎn)換將未初始化的內(nèi)存轉(zhuǎn)換為MaybeUninit<T>衡瓶。
use std::mem::MaybeUninit;
// Create an explicitly uninitialized reference. The compiler knows that data inside
// a `MaybeUninit<T>` may be invalid, and hence this is not UB:
// 獲得一個(gè)未初始化的i32引用類(lèi)型內(nèi)存
let mut x = MaybeUninit::<&i32>::uninit();
// Set it to a valid value.
// 將&0寫(xiě)入變量,完成初始化
x.write(&0);
// Extract the initialized data -- this is only allowed *after* properly
// initializing `x`!
// 將初始化后的變量解封裝供后繼的代碼使用牲证。
let x = unsafe { x.assume_init() };
以上代碼哮针,編譯器不會(huì)對(duì)x.write進(jìn)行報(bào)警,這是MaybeUninit<T>的最重要的應(yīng)用坦袍,這個(gè)例子展示了RUST如何給未初始化內(nèi)存賦值的處理方式十厢。調(diào)用assume_init前,必須保證變量已經(jīng)被正確初始化捂齐。
更復(fù)雜的例子:
use std::mem::{self, MaybeUninit};
let data = {
// Create an uninitialized array of `MaybeUninit`. The `assume_init` is
// safe because the type we are claiming to have initialized here is a
// bunch of `MaybeUninit`s, which do not require initialization.
// data在聲明后實(shí)際上就已經(jīng)初始化完畢蛮放。
let mut data: [MaybeUninit<Vec<u32>>; 1000] = unsafe {
//這里注意實(shí)際調(diào)用是MaybeUninit::<[MaybeUninit<Vec<u32>>;1000]>::uninit(), RUST的類(lèi)型推斷機(jī)制完成了泛型實(shí)例化
MaybeUninit::uninit().assume_init()
};
// Dropping a `MaybeUninit` does nothing. Thus using raw pointer
// assignment instead of `ptr::write` does not cause the old
// uninitialized value to be dropped. Also if there is a panic during
// this loop, we have a memory leak, but there is no memory safety
// issue.
for elem in &mut data[..] {
elem.write(vec![42]);
}
// Everything is initialized. Transmute the array to the
// initialized type.
// 直接用transmute完成整個(gè)數(shù)組類(lèi)型的轉(zhuǎn)換
unsafe { mem::transmute::<_, [Vec<u32>; 1000]>(data) }
};
assert_eq!(&data[0], &[42]);
下面例子說(shuō)明一塊內(nèi)存被 MaybeUnint<T>封裝后,編譯器將不再對(duì)其做釋放奠宜,必須在代碼中顯式釋放:
use std::mem::MaybeUninit;
use std::ptr;
// Create an uninitialized array of `MaybeUninit`. The `assume_init` is
// safe because the type we are claiming to have initialized here is a
// bunch of `MaybeUninit`s, which do not require initialization.
let mut data: [MaybeUninit<String>; 1000] = unsafe { MaybeUninit::uninit().assume_init() };
// 初始化了500個(gè)String變量
let mut data_len: usize = 0;
for elem in &mut data[0..500] {
//write沒(méi)有將所有權(quán)轉(zhuǎn)移出ManuallyDrop
elem.write(String::from("hello"));
data_len += 1;
}
// For each item in the array, drop if we allocated it.
//rust不能自動(dòng)去釋放已經(jīng)申請(qǐng)的String, 必須手工調(diào)用drop_in_place釋放
for elem in &mut data[0..data_len] {
unsafe { ptr::drop_in_place(elem.as_mut_ptr()); }
}
上例中包颁,在沒(méi)有assume_init()調(diào)用的情況下,必須手工調(diào)用drop_in_place釋放內(nèi)存压真。
MaybeUninit<T>是一個(gè)非常重要的類(lèi)型結(jié)構(gòu)娩嚼,未初始化內(nèi)存是編程中不可避免要遇到的情況,MaybeUninit<T>也就是RUST編程中必須熟練使用的一個(gè)類(lèi)型滴肿。
mem模塊函數(shù)庫(kù)
mem::zeroed<T>() -> T
此函數(shù)用MaybeUninit::zeroed獲取全零內(nèi)存后岳悟,調(diào)用assume_init(), 返回一個(gè)清零的T變量,要確認(rèn)全零是一種T類(lèi)型合理的初始化才可用
mem::uninitialized<T>() -> T
用MaybeUnint::uninit獲取一塊未初始化內(nèi)存泼差,然后調(diào)用assume_init(), 此時(shí)內(nèi)存徹底未初始化贵少。
mem::take<T: Default>(dest: &mut T) -> T
將dest設(shè)置為默認(rèn)內(nèi)容(不改變所有權(quán))和屎,用一個(gè)新變量返回dest的內(nèi)容。
mem::replace<T>(dest: &mut T, src: T) -> T
用src的內(nèi)容賦值dest(不改變所有權(quán))春瞬,用一個(gè)新變量返回dest的內(nèi)容柴信。
mem::transmute<T,U>(src: T) -> U
直接將T類(lèi)型內(nèi)存轉(zhuǎn)化為U類(lèi)型內(nèi)存。 類(lèi)似C語(yǔ)言的&(U *(&src))操作宽气。
mem::transmute_copy<T, U>(src: &T) -> U
新建類(lèi)型U的變量随常,并把src的內(nèi)容拷貝到U。
mem::forget<T>(t:T)
通知RUST不做變量的drop操作萄涯,代碼用ManuallyDrop完成
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
基本就是直接調(diào)用intrinsic模塊的同名函數(shù)
mem::drop<T>(_x:T)
釋放內(nèi)存
ptr模塊再探
ptr::read<T>(src: *const T) -> T
此函數(shù)用已有的類(lèi)型復(fù)制出一個(gè)新的類(lèi)型實(shí)體,對(duì)于不支持Copy Trait的類(lèi)型绪氛,read函數(shù)是RUST實(shí)現(xiàn)未知類(lèi)型變量的復(fù)制的一種方法,此函數(shù)作為內(nèi)存函數(shù)take(), replace(), transmute_copy()的基礎(chǔ)涝影,底層使用intrisic::copy_no_overlapping支持,代碼分析已經(jīng)在前面章節(jié)完成
ptr::read_unaligned<T>(src: *const T) -> T
當(dāng)數(shù)據(jù)結(jié)構(gòu)中有未內(nèi)存對(duì)齊的成員變量時(shí)枣察,需要用此函數(shù)讀取內(nèi)容并轉(zhuǎn)化為內(nèi)存對(duì)齊的變量。否則會(huì)引發(fā)UB(undefined behaiver) 如下例:
/// Read a usize value from a byte buffer:
use std::mem;
fn read_usize(x: &[u8]) -> usize {
assert!(x.len() >= mem::size_of::<usize>());
let ptr = x.as_ptr() as *const usize;
unsafe { ptr.read_unaligned() }
}
例子中燃逻,為了從byte串中讀取一個(gè)usize序目,需要用read_unaligned來(lái)獲取值,不能象C語(yǔ)言那樣通過(guò)指針類(lèi)型轉(zhuǎn)換直接獲取值伯襟。
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);
//必須調(diào)用forget猿涨,這里所有權(quán)已經(jīng)轉(zhuǎn)移。不允許再對(duì)src做drop操作
intrinsics::forget(src);
}
}
write函數(shù)本質(zhì)上就是一個(gè)所有權(quán)轉(zhuǎn)移的操作姆怪。完成src到dst的淺拷貝叛赚,然后調(diào)用了forget(src), 這使得src的Drop不再被調(diào)用(也規(guī)避src類(lèi)型如果有引用導(dǎo)致的重復(fù)釋放問(wèn)題)。從而將所有權(quán)轉(zhuǎn)移到dst稽揭。此函數(shù)是mem::replace俺附, mem::transmute_copy的基礎(chǔ)。底層由intrisic:: copy_no_overlapping支持溪掀。
這個(gè)函數(shù)中事镣,如果dst已經(jīng)初始化過(guò),那原dst變量的所有權(quán)將被丟失掉膨桥,有可能引發(fā)內(nèi)存泄漏蛮浑。
pub const fn
replace<T>(dest: &mut T, src: T) -> T {
unsafe {
let result = ptr::read(dest);
ptr::write(dest, src);
result
}
}
上面的函數(shù)不會(huì)導(dǎo)致內(nèi)存泄漏問(wèn)題。因?yàn)閞ead本身做了一個(gè)已有變量的復(fù)制只嚣。
ptr::write_unaligned<T>(dst: *mut T, src: T)
與read_unaligned相對(duì)應(yīng)沮稚。舉例如下:
#[repr(packed, C)]
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.
// 對(duì)于結(jié)構(gòu)中字節(jié)沒(méi)有按照2冪次對(duì)齊的成員,要用addr_of_mut!宏來(lái)獲得地址册舞,無(wú)法用取引用的方式蕴掏。
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)
因?yàn)橛?amp;獲得引用必須是字節(jié)按照2的冪次對(duì)齊的地址,所以用這個(gè)宏獲取非地址對(duì)齊的變量地址
ptr::macro addr_of_mut($place:expr)
作用同上。
指針的通用函數(shù)請(qǐng)參考Rust庫(kù)函數(shù)參考
NonNull 與MaybeUninit<T>相關(guān)函數(shù)
NonNull<T>::as_uninit_ref<`a>(&self) -> &`a MaybeUninit<T>
NonNull與MaybeUninit的引用基本就是直接轉(zhuǎn)換的關(guān)系盛杰,一體雙面
NonNull<T>::as_uninit_mut<`a>(&self) -> &`a mut MaybeUninit<T>
NonNull<[T]>::as_uninit_slice<'a>(&self) -> &'a [MaybeUninit<T>]
NonNull<[T]>::as_uninit_slice_mut<'a>(&self) -> &'a mut [MaybeUninit<T>]
Unique
Unique類(lèi)型結(jié)構(gòu)定義如下
#[repr(transparent)]
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對(duì)比挽荡,Unique多了PhantomData<T>類(lèi)型變量。這個(gè)定義使得編譯器知曉即供,Unique<T>擁有了pointer指向的內(nèi)存的所有權(quán)定拟,NonNull<T>沒(méi)有這個(gè)特性。具備所有權(quán)后逗嫡,Unique<T>可以實(shí)現(xiàn)Send, Sync等Trait青自。
指針在被Unique封裝前,必須保證是NonNull的
RUST用Allocator申請(qǐng)出來(lái)的內(nèi)存的所有權(quán)用Unique<T>做了綁定驱证,使得內(nèi)存進(jìn)入了RUST的所有權(quán)和借用系統(tǒng)延窜。
Unique<T>創(chuàng)建舉例:
//下面的代碼前文已經(jīng)解釋過(guò),請(qǐng)參考
pub fn try_new_uninit_in(alloc: A) -> Result<Box<mem::MaybeUninit<T>, A>, AllocError> {
let layout = Layout::new::<mem::MaybeUninit<T>>();
let ptr = alloc.allocate(layout)?.cast();
unsafe { Ok(Box::from_raw_in(ptr.as_ptr(), alloc)) }
}
pub unsafe fn from_raw_in(raw: *mut T, alloc: A) -> Self {
Box(unsafe { Unique::new_unchecked(raw) }, alloc)
}
以上代碼是Box<T>的創(chuàng)建關(guān)聯(lián)函數(shù)抹锄,從代碼可以分析比較完整的RUST的動(dòng)態(tài)內(nèi)存申請(qǐng)類(lèi)型轉(zhuǎn)換過(guò)程是逆瑞,GlobalAlloc::alloc申請(qǐng)返回* mut u8指針,Allocator::allocate() 返回NonNull<[U8]>類(lèi)型指針伙单,由NonNull::cast()轉(zhuǎn)化為NonNull<MaybeUninit<T>>類(lèi)型获高,然后由NonNull<MaybeUninit<T>> 重新拆封裝建立Unique<MaybeUninit<T>>類(lèi)型。至此车份,申請(qǐng)的動(dòng)態(tài)內(nèi)存進(jìn)入了RUST的所有權(quán)識(shí)別領(lǐng)域
Unique::cast<U>(self)->Unique<U>
類(lèi)型轉(zhuǎn)換谋减,程序員應(yīng)該保證T和U的內(nèi)存布局相同
Unique::<T>::new(* mut T)->Option<Self>
此函數(shù)內(nèi)部判斷* mut T是否為0值
Unique::<T>::new_unchecked(* mut T)->Self
封裝* mut T, 調(diào)用代碼應(yīng)該保證* mut T的安全性
Unique::as_ptr(self)->* mut T
Unique::as_ref(&self)->& T
因?yàn)閁nique具備所有權(quán),此處&T的生命周期與self相同扫沼,不必特別聲明聲明周期
Unique::as_mut(&mut self)->& mut T
同上
所有權(quán)轉(zhuǎn)移的底層實(shí)現(xiàn)
所有權(quán)的轉(zhuǎn)移實(shí)際上是兩步:1.棧上內(nèi)存的淺拷貝;2:原先的變量置標(biāo)志表示所有權(quán)已轉(zhuǎn)移庄吼。置標(biāo)志的變量如果沒(méi)有重新綁定其他變量缎除,則在生命周期結(jié)束的時(shí)候被drop。 引用及指針自身也是一個(gè)isize的值變量总寻,也有所有權(quán)器罐,也具備生命周期。
變量調(diào)用drop的時(shí)機(jī)
如下例子:
struct TestPtr {a: i32, b:i32}
impl Drop for TestPtr {
fn drop(&mut self) {
println!("{} {}", self.a, self.b);
}
}
fn main() {
let test = Box::new(TestPtr{a:1,b:2});
let test1 = *test;
let mut test2 = TestPtr{a:2, b:3};
//此行代碼會(huì)導(dǎo)致先釋放test2擁有所有權(quán)的變量渐行,然后再給test2賦值轰坊。代碼后的輸出會(huì)給出證據(jù)
//將test1的所有權(quán)轉(zhuǎn)移給test2,無(wú)疑代表著test2現(xiàn)有的所有權(quán)會(huì)在后繼無(wú)法訪問(wèn)祟印,因此drop被立即調(diào)用肴沫。
test2 = test1;
println!("{:?}", test2);
}
輸出:
2 3
TestPtr { a: 1, b: 2 }
1 2
小結(jié)
在RUST標(biāo)準(zhǔn)庫(kù)的ptr, mem,alloc模塊提供了RUST內(nèi)存的底層操作蕴忆。內(nèi)存的底層操作是其他RUST庫(kù)模塊的基礎(chǔ)設(shè)施颤芬。不能理解內(nèi)存的底層操作,就無(wú)法駕馭RUST完成較復(fù)雜的任務(wù)。