本书github链接:
inside-rust-std-library
前面章节参见:
深入RUST标准库内核(序言) - 简书 (jianshu.com)
深入RUST标准库内核(一 概述) - 简书 (jianshu.com)
深入RUST标准库内核(二 内存)—Layout/原生指针 - 简书 (jianshu.com)
深入RUST标准库内核(二 内存)—NonNull<T>/申请及释放 - 简书 (jianshu.com)
深入RUST标准库内核(二 内存)—mem模块/MaybeUninit<T> - 简书 (jianshu.com)
深入RUST标准库内核 (三 基础Trait) 编译器内置Trait - 简书 (jianshu.com)
深入RUST标准库内核(三 基础Trait)— Ops Trait - 简书 (jianshu.com)
深入RUST标准库内核(三 基本Trait)—Range - 简书 (jianshu.com)
RUST的Index 运算符代码分析
数组下标符号[]由Index, IndexMut两个Trait完成重载。数组下标符号重载使得程序更有可读性。两个Trait如下定义:
pub trait Index<Idx: ?Sized> {
/// The returned type after indexing.
type Output: ?Sized;
/// 若果传入的参数超过内存界限将马上引发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;
}
切片数据结构[T]的Index实现
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实现[T]的ops::Index。SliceIndex主要是为了实现下标即支持用usize类型取出单一元素,又支持用Range类型取出子slice。
显然,针对不同的Index<Idx>中的泛型Idx,需要实现不同的处理逻辑。SliceIndex的引入是典型的处理这个需求的设计方式。即如果对某一类型实现一个具有泛型的Trait时,如果对于Trait的泛型实例化不同类型,会带来处理逻辑的不同。那就再定义一个辅助Trait,为前Trait的实例化类型实现辅助Trait,在这个辅助Trait的实现中实现不同的处理逻辑。辅助Trait和Trait之间的定义相关性即可参考SliceIndex和Index的定义相关性。
mod private_slice_index {
use super::ops;
//在私有模块中定义一个Sealed Trait,后继的SliceIndex继承Sealed。
//带来的结果是只有在本模块实现了Sealed Trait的类型才能实现SliceIndex
//即使SliceIndex是公有定义,其他类型仍然不能够实现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是序列的头指针,后面的具体实现会看到为什么用 *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> {
// 必须转化为*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 {
//相当于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 此处应该可以用get,但应该是编译器内置支持,为了效率直接使用了内置的数组下标表示。
&(*slice)[self]
}
fn index_mut(self, slice: &mut [T]) -> &mut T {
// N.B., use intrinsic indexing
&mut (*slice)[self]
}
}
以上就是针对[T]的以无符号数作为下标取出单一元素的ops::Index 及 ops::IndexMut的底层实现,从slice中取出单一元素必须应用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的内存操作形成* 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]转化为切片引用
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) }
}
}
以上是实现用Range从slice中取出子slice的实现。其他如RangeTo等与Range大同小异。
小结
对于切片的Index, 整体上,需要将切片类型的引用转换为slice元素类型的原生指针,然后对原生指针做加减操作,再根据需要重新建立元素类型或作切片类型的原生指针,然后将原生指针转换为引用。由此可见,ptr和mem模块的熟练使用是必须掌握的。
数组数据结构[T;N]的ops::Index实现
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]转化为了切片[T], 所以数组的Index就是[T]的Index实现