深入RUST標(biāo)準(zhǔn)庫內(nèi)核(三 基礎(chǔ)Trait)—Index Trait

本書github鏈接:
inside-rust-std-library
前面章節(jié)參見:
深入RUST標(biāo)準(zhǔn)庫內(nèi)核(序言) - 簡書 (jianshu.com)
深入RUST標(biāo)準(zhǔn)庫內(nèi)核(一 概述) - 簡書 (jianshu.com)
深入RUST標(biāo)準(zhǔn)庫內(nèi)核(二 內(nèi)存)—Layout/原生指針 - 簡書 (jianshu.com)
深入RUST標(biāo)準(zhǔn)庫內(nèi)核(二 內(nèi)存)—NonNull<T>/申請及釋放 - 簡書 (jianshu.com)
深入RUST標(biāo)準(zhǔn)庫內(nèi)核(二 內(nèi)存)—mem模塊/MaybeUninit<T> - 簡書 (jianshu.com)
深入RUST標(biāo)準(zhǔn)庫內(nèi)核 (三 基礎(chǔ)Trait) 編譯器內(nèi)置Trait - 簡書 (jianshu.com)
深入RUST標(biāo)準(zhǔn)庫內(nèi)核(三 基礎(chǔ)Trait)— Ops Trait - 簡書 (jianshu.com)
深入RUST標(biāo)準(zhǔn)庫內(nèi)核(三 基本Trait)—Range - 簡書 (jianshu.com)

RUST的Index 運算符代碼分析

數(shù)組下標(biāo)符號[]由Index, IndexMut兩個Trait完成重載棒呛。數(shù)組下標(biāo)符號重載使得程序更有可讀性机蔗。兩個Trait如下定義:

pub trait Index<Idx: ?Sized> {
    /// The returned type after indexing.
    type Output: ?Sized;

    /// 若果傳入的參數(shù)超過內(nèi)存界限將馬上引發(fā)panic
    fn index(&self, index: Idx) -> &Self::Output;
}

pub trait IndexMut<Idx: ?Sized>: Index<Idx> {
    fn index_mut(&mut self, index: Idx) -> &mut Self::Output;
}

切片數(shù)據(jù)結(jié)構(gòu)[T]的Index實現(xiàn)

impl<T, I> ops::Index<I> for [T]
where
    I: SliceIndex<[T]>,
{
    type Output = I::Output;

    fn index(&self, index: I) -> &I::Output {
        index.index(self)
    }
}

impl<T, I> ops::IndexMut<I> for [T]
where
    I: SliceIndex<[T]>,
{
    fn index_mut(&mut self, index: I) -> &mut I::Output {
        index.index_mut(self)
    }
}

需要依賴SliceIndex Trait實現(xiàn)[T]的ops::Index。SliceIndex主要是為了實現(xiàn)下標(biāo)即支持用usize類型取出單一元素敷存,又支持用Range類型取出子slice肩杈。
顯然柴我,針對不同的Index<Idx>中的泛型Idx,需要實現(xiàn)不同的處理邏輯扩然。SliceIndex的引入是典型的處理這個需求的設(shè)計方式艘儒。即如果對某一類型實現(xiàn)一個具有泛型的Trait時,如果對于Trait的泛型實例化不同類型,會帶來處理邏輯的不同界睁。那就再定義一個輔助Trait觉增,為前Trait的實例化類型實現(xiàn)輔助Trait,在這個輔助Trait的實現(xiàn)中實現(xiàn)不同的處理邏輯翻斟。輔助Trait和Trait之間的定義相關(guān)性即可參考SliceIndex和Index的定義相關(guān)性逾礁。

mod private_slice_index {
    use super::ops;
    //在私有模塊中定義一個Sealed Trait,后繼的SliceIndex繼承Sealed访惜。
    //帶來的結(jié)果是只有在本模塊實現(xiàn)了Sealed Trait的類型才能實現(xiàn)SliceIndex
    //即使SliceIndex是公有定義嘹履,其他類型仍然不能夠?qū)崿F(xiàn)SliceIndex
    pub trait Sealed {}

    impl Sealed for usize {}
    impl Sealed for ops::Range<usize> {}
    impl Sealed for ops::RangeTo<usize> {}
    impl Sealed for ops::RangeFrom<usize> {}
    impl Sealed for ops::RangeFull {}
    impl Sealed for ops::RangeInclusive<usize> {}
    impl Sealed for ops::RangeToInclusive<usize> {}
    impl Sealed for (ops::Bound<usize>, ops::Bound<usize>) {}
}

pub unsafe trait SliceIndex<T: ?Sized>: private_slice_index::Sealed {
    /// 此類型通常為T或者T的引用,切片债热,原生指針類型
    type Output: ?Sized;

    // 從slice變量中用self獲取Option<Output>變量
    fn get(self, slice: &T) -> Option<&Self::Output>;

    fn get_mut(self, slice: &mut T) -> Option<&mut Self::Output>;

    //slice是序列的頭指針砾嫉,后面的具體實現(xiàn)會看到為什么用 *const T
    unsafe fn get_unchecked(self, slice: *const T) -> *const Self::Output;

    unsafe fn get_unchecked_mut(self, slice: *mut T) -> *mut Self::Output;
    
    //如果self超出slice的安全范圍,會panic
    fn index(self, slice: &T) -> &Self::Output;

    fn index_mut(self, slice: &mut T) -> &mut Self::Output;
}

unsafe impl<T> SliceIndex<[T]> for usize {
    type Output = T;

    fn get(self, slice: &[T]) -> Option<&T> {
        // 必須轉(zhuǎn)化為*const T才能夠用指針加方式來獲取
        if self < slice.len() { unsafe { Some(&*self.get_unchecked(slice)) } } else { None }
    }

    fn get_mut(self, slice: &mut [T]) -> Option<&mut T> {
        if self < slice.len() { unsafe { Some(&mut *self.get_unchecked_mut(slice)) } } else { None }
    }

    unsafe fn get_unchecked(self, slice: *const [T]) -> *const T {
        //相當(dāng)于C的指針加操作
        unsafe { slice.as_ptr().add(self) }
    }

    unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut T {
        unsafe { slice.as_mut_ptr().add(self) }
    }

    fn index(self, slice: &[T]) -> &T {
        // N.B., use intrinsic indexing 此處應(yīng)該可以用get,但應(yīng)該是編譯器內(nèi)置支持窒篱,為了效率直接使用了內(nèi)置的數(shù)組下標(biāo)表示焕刮。
        &(*slice)[self]
    }

    fn index_mut(self, slice: &mut [T]) -> &mut T {
        // N.B., use intrinsic indexing
        &mut (*slice)[self]
    }
}

以上就是針對[T]的以無符號數(shù)作為下標(biāo)取出單一元素的ops::Index 及 ops::IndexMut的底層實現(xiàn),從slice中取出單一元素必須應(yīng)用ptr的操作舌剂。

unsafe impl<T> SliceIndex<[T]> for ops::Range<usize> {
    type Output = [T];

    fn get(self, slice: &[T]) -> Option<&[T]> {
        if self.start > self.end || self.end > slice.len() {
            None
        } else {
            unsafe { Some(&*self.get_unchecked(slice)) }
        }
    }

    fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> {
        if self.start > self.end || self.end > slice.len() {
            None
        } else {
            unsafe { Some(&mut *self.get_unchecked_mut(slice)) }
        }
    }

    unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
        // 利用ptr的內(nèi)存操作形成* const [T] 原生指針
        unsafe { ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), self.end - self.start) }
    }

    unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] {
        unsafe {
            ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), self.end - self.start)
        }
    }

    fn index(self, slice: &[T]) -> &[T] {
        if self.start > self.end {
            slice_index_order_fail(self.start, self.end);
        } else if self.end > slice.len() {
            slice_end_index_len_fail(self.end, slice.len());
        }
        //將* const [T]轉(zhuǎn)化為切片引用
        unsafe { &*self.get_unchecked(slice) }
    }

    fn index_mut(self, slice: &mut [T]) -> &mut [T] {
        if self.start > self.end {
            slice_index_order_fail(self.start, self.end);
        } else if self.end > slice.len() {
            slice_end_index_len_fail(self.end, slice.len());
        }
        unsafe { &mut *self.get_unchecked_mut(slice) }
    }
}

以上是實現(xiàn)用Range從slice中取出子slice的實現(xiàn)济锄。其他如RangeTo等與Range大同小異。

小結(jié)

對于切片的Index, 整體上霍转,需要將切片類型的引用轉(zhuǎn)換為slice元素類型的原生指針荐绝,然后對原生指針做加減操作,再根據(jù)需要重新建立元素類型或作切片類型的原生指針避消,然后將原生指針轉(zhuǎn)換為引用低滩。由此可見,ptr和mem模塊的熟練使用是必須掌握的岩喷。

數(shù)組數(shù)據(jù)結(jié)構(gòu)[T;N]的ops::Index實現(xiàn)

impl<T, I, const N: usize> Index<I> for [T; N]
where
    [T]: Index<I>,
{
    type Output = <[T] as Index<I>>::Output;

    fn index(&self, index: I) -> &Self::Output {
        Index::index(self as &[T], index)
    }
}

impl<T, I, const N: usize> IndexMut<I> for [T; N]
where
    [T]: IndexMut<I>,
{
    fn index_mut(&mut self, index: I) -> &mut Self::Output {
        IndexMut::index_mut(self as &mut [T], index)
    }
}

以上恕沫, self as &[T] 即把[T;N]轉(zhuǎn)化為了切片[T], 所以數(shù)組的Index就是[T]的Index實現(xiàn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市纱意,隨后出現(xiàn)的幾起案子婶溯,更是在濱河造成了極大的恐慌,老刑警劉巖偷霉,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件迄委,死亡現(xiàn)場離奇詭異,居然都是意外死亡类少,警方通過查閱死者的電腦和手機(jī)叙身,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來硫狞,“玉大人信轿,你說我怎么就攤上這事晃痴。” “怎么了财忽?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵倘核,是天一觀的道長。 經(jīng)常有香客問我定罢,道長笤虫,這世上最難降的妖魔是什么旁瘫? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任祖凫,我火速辦了婚禮,結(jié)果婚禮上酬凳,老公的妹妹穿的比我還像新娘惠况。我一直安慰自己,他們只是感情好宁仔,可當(dāng)我...
    茶點故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布稠屠。 她就那樣靜靜地躺著,像睡著了一般翎苫。 火紅的嫁衣襯著肌膚如雪权埠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天煎谍,我揣著相機(jī)與錄音攘蔽,去河邊找鬼。 笑死呐粘,一個胖子當(dāng)著我的面吹牛满俗,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播作岖,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼唆垃,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了痘儡?” 一聲冷哼從身側(cè)響起辕万,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎沉删,沒想到半個月后渐尿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡丑念,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年涡戳,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片脯倚。...
    茶點故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡渔彰,死狀恐怖嵌屎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情恍涂,我是刑警寧澤宝惰,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站再沧,受9級特大地震影響尼夺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜炒瘸,卻給世界環(huán)境...
    茶點故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一淤堵、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧顷扩,春花似錦拐邪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至婶芭,卻和暖如春东臀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背犀农。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工惰赋, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人井赌。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓谤逼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親仇穗。 傳聞我的和親對象是個殘疾皇子流部,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,585評論 2 359

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