深入RUST標(biāo)準(zhǔn)庫(kù)內(nèi)核(二 內(nèi)存)—mem模塊/MaybeUninit<T>

本書(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)景:

  1. 作為MaybeUninit的內(nèi)部結(jié)構(gòu)锤悄,對(duì)未初始化的內(nèi)存做一個(gè)保護(hù)和標(biāo)識(shí)韧骗。
  2. 希望由代碼顯式釋放變量時(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ù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末站蝠,一起剝皮案震驚了整個(gè)濱河市汰具,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌菱魔,老刑警劉巖留荔,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異澜倦,居然都是意外死亡聚蝶,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)肥隆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)既荚,“玉大人,你說(shuō)我怎么就攤上這事栋艳∏∑福” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵吸占,是天一觀的道長(zhǎng)晴叨。 經(jīng)常有香客問(wèn)我,道長(zhǎng)矾屯,這世上最難降的妖魔是什么兼蕊? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮件蚕,結(jié)果婚禮上孙技,老公的妹妹穿的比我還像新娘。我一直安慰自己排作,他們只是感情好牵啦,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著妄痪,像睡著了一般哈雏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上衫生,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天裳瘪,我揣著相機(jī)與錄音,去河邊找鬼罪针。 笑死,一個(gè)胖子當(dāng)著我的面吹牛站故,可吹牛的內(nèi)容都是我干的毅舆。 我是一名探鬼主播愈腾,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼憋活,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了悦即?” 一聲冷哼從身側(cè)響起橱乱,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤辜梳,失蹤者是張志新(化名)和其女友劉穎昵济,沒(méi)想到半個(gè)月后乏沸,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體桐愉,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宗挥,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年契耿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了螃征。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡踢械,死狀恐怖魄藕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤荷荤,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布蕴纳,位于F島的核電站,受9級(jí)特大地震影響翻翩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嫂冻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一胶征、第九天 我趴在偏房一處隱蔽的房頂上張望睛低。 院中可真熱鬧服傍,春花似錦、人聲如沸吹零。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)泣懊。三九已至麻惶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間卡啰,已是汗流浹背警没。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留亡脸,地道東北人树酪。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓续语,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親疮茄。 傳聞我的和親對(duì)象是個(gè)殘疾皇子根暑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容