zoniony / zoniony.github.io

blog
1 stars 0 forks source link

rust slice切片 #12

Open zoniony opened 4 years ago

zoniony commented 4 years ago

动漫真尼玛胃疼 看完漫画妹妹党61✌️ 心疼姐姐

RangeFull Slice

来看看最简单的切片(slice)

let a = [1, 2, 3];
let mut b = &a[..];

编译器会把数组的地址和长度作为参数, 数组是编译器可确定大小的,还不清楚为什么要把data.rel.ro存文件名字符串段传进去

调用core::ops::indexindex方法

   0x55555555bc0b <Std::main+43>   mov    rdi, rcx  ;数组 ptr
   0x55555555bc0e <Std::main+46>   mov    esi, 0x3  ;length
   0x55555555bc13 <Std::main+51>   mov    rdx, rax  ;.data.rel.ro段的?
 → 0x55555555bc16 <Std::main+54>   call   0x55555555a830 <core::slice::<impl core::ops::index::Index<I> for [T]>::index>    ;调用

先看Index tarit, 返回 index 后的类型,包含 index 方法

container[index] is actually syntactic sugar for *container.index(index), but only when used as an immutable value. If a mutable value is requested, IndexMut is used instead. This allows nice things such as let value = v[index] if the type of value implements Copy.

平常用的 container[index]也不过是*container.index(index)的语法糖

那么这里的&slice[..] 等于 &*slice.index(std::ops::RangeFull)?

pub trait Index<Idx: ?Sized> {
    /// The returned type after indexing.
    #[stable(feature = "rust1", since = "1.0.0")]
    type Output: ?Sized;

    /// Performs the indexing (`container[index]`) operation.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn index(&self, index: Idx) -> &Self::Output;
}

这里是为数组[T]的实现, 调用 indextarit 的index 方法

index: I 类型是 core::ops::range::RangeFull

self此时代表&[i32]切片

调用<<core::ops::range::RangeFull as core::slice::SliceIndex<[T]>>::index>

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

    #[inline]
    fn index(&self, index: I) -> &I::Output {
        index.index(self)
    }
}

Slice限定的SliceIndex如下,下面列举了作为 index 应该满足的调用

impl<T> SliceIndex<[T]> for ops::RangeFull {
    type Output = [T];

    #[inline]
    fn get(self, slice: &[T]) -> Option<&[T]> {
        Some(slice)
    }

    #[inline]
    fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> {
        Some(slice)
    }

    #[inline]
    unsafe fn get_unchecked(self, slice: &[T]) -> &[T] {
        slice
    }

    #[inline]
    unsafe fn get_unchecked_mut(self, slice: &mut [T]) -> &mut [T] {
        slice
    }

    #[inline]
    fn index(self, slice: &[T]) -> &[T] {
        slice
    }

    #[inline]
    fn index_mut(self, slice: &mut [T]) -> &mut [T] {
        slice
    }
}

ops::Range

稍微改一下

let a = [1, 2, 3];
let mut b = &a[0..1];

因为是数组引索,还是调用impl Index for [T],传入数组信息

   0x55555555bc13 <Std::main+51>   mov    rdi, rcx
   0x55555555bc16 <Std::main+54>   mov    esi, 0x3
   0x55555555bc1b <Std::main+59>   mov    rdx, rax
 → 0x55555555bc1e <Std::main+62>   call   0x55555555a830 <core::slice::<impl core::ops::index::Index<I> for [T]>::index>

这里有所变化,这里传入的参数多了个 beginend

[T] (
   $rdi = 0x0000000000000000,
   $rsi = 0x0000000000000002,
   $rdx = 0x00007fffffffdc80 → 0x0000000100000000,
   $rcx = 0x0000000000000004
)

分别处理可变不可变两种情况,可以看到最后还是用转换成用指针操作, 还是用了unsafe = =

impl<T> SliceIndex<[T]> for ops::Range<usize> {
    type Output = [T];
  //.....省略

    #[inline]
    unsafe fn get_unchecked(self, slice: &[T]) -> &[T] {
        from_raw_parts(slice.as_ptr().add(self.start), self.end - self.start)
    }
    #[inline]
    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_index_len_fail(self.end, slice.len());
        }
        unsafe { self.get_unchecked(slice) }
    }

    #[inline]
    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_index_len_fail(self.end, slice.len());
        }
        unsafe { self.get_unchecked_mut(slice) }
    }
}

当然还有一些检查

#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] {
    debug_assert!(is_aligned_and_not_null(data), "attempt to create unaligned or null slice");
    debug_assert!(
        mem::size_of::<T>().saturating_mul(len) <= isize::MAX as usize,
        "attempt to create slice covering at least half the address space"
    );
    &*ptr::slice_from_raw_parts(data, len)
}

slice

let a = [1, 2, 3];
let b = &a[0..1];
let c = &a[..][0]
let b = [1, 2, 3][..][0];

再来看一个迷惑的例子= =别跟我说这是三维数组 (

切片的表示不止&slice[..]一种, [T][..]也能表示 因为[..]返回一个切片

总结

例子

比如std 官方例子就很生动了

此时Nucleotide相当于FullRange NucleotideCount相当于[T]

use std::ops::Index;

impl Index<Nucleotide> for NucleotideCount {
    type Output = usize;

    fn index(&self, nucleotide: Nucleotide) -> &Self::Output {
        match nucleotide {
            Nucleotide::A => &self.a,
            Nucleotide::C => &self.c,
            Nucleotide::G => &self.g,
            Nucleotide::N => &self.n,   
        }
    }
}

enum Nucleotide {
    A,
    C,
    G,
    N,
}

struct NucleotideCount {
    a: usize,
    c: usize,
    g: usize,
    n: usize,
}

fn main() {
    let nucleotide_count = NucleotideCount {a: 14, c: 9, g: 10, n: 12};
    println!("{}", nucleotide_count[Nucleotide::A]);
    println!("{}", nucleotide_count[Nucleotide::C]);
    println!("{}", nucleotide_count[Nucleotide::G]);
    println!("{}", nucleotide_count[Nucleotide::N]);

}