添字アクセスの舞台裏について。

簡単な例

以下は各種の添字アクセス。


let arr: [i32;6] = [0, 1, 2, 3, 4, 5];
let value: i32 = arr[2]; // 2
let slice: &[i32] = &arr[..];    // 0, 1, 2, 3, 4, 5
let slice: &[i32] = &arr[2..];   // 2, 3, 4, 5
let slice: &[i32] = &arr[..4];   // 0, 1, 2, 3
let slice: &[i32] = &arr[2..4];  // 2, 3
let slice: &[i32] = &arr[..=4];  // 0, 1, 2, 3, 4
let slice: &[i32] = &arr[2..=4]; // 2, 3, 4
let slice: &[i32] = &arr[(Bound::Excluded(2), Bound::Unbounded)]; // 3, 4, 5

添字式

添字式は IndexIndexMut トレイトを通して処理される。スライス型や配列型で添字式が使えるのは、これらを実装しているためである。なお、添字の型と出力の型はパラメトリックになっている。スライスと配列の場合、添字の型は SliceIndex トレイトを実装している型、出力の型はその関連型から導出される型になる。

Index

読取系の添字式を処理するトレイト。

型パラメタ Idx が添字の型を、関連型 Output が出力の型を表す。


pub trait Index<Idx>
where
    Idx: ?Sized,
{
    type Output: ?Sized;

    // Required method
    fn index(&self, index: Idx) -> &Self::Output;
}

IndexMut

更新系の添字式を処理するトレイト。

このトレイトの実装には Index トレイトの実装が前提になっている。


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

スライス

スライス [T]Index<I>IndexMut<I> を実装している。

ここで、添字の型 I には SliceIndex<[T]> の実装が必要。


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

    /* ... */
}

impl<T, I> IndexMut<I> for [T]
where
    I: SliceIndex<[T]>,
{
    /* ... */
}

配列

配列 [T; N]Index<I>IndexMut<I> を実装している。

これらの実装はスライス [T] の実装が基になっている。


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

    /* ... */
}

impl<T, I, const N: usize> IndexMut<I> for [T; N]
where
	[T]: IndexMut<I>,
{
    /* ... */
}

添字

前述の通り、スライスや配列の添字は SliceIndex トレイトを持つ。usize 型や各種の範囲型が添字になれるのは、これを実装しているためである。そして、それぞれの関連型から出力の型が決まる。これは usize 型なら要素の型、範囲型なら要素のスライス型である。

SliceIndex

スライスや配列の添字として使える型をまとめるトレイト。

SliceIndex<T> は型パラメタ T への添字として使える。


pub unsafe trait SliceIndex<T>: Sealed
where
    T: ?Sized,
{
    type Output: ?Sized;

    /* ... */
}

usize

添字が usize 型の場合、出力は要素の型 T になる。


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

    /* ... */
}

範囲型

添字が範囲型の場合、出力は要素のスライス型 [T] になる。


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

    /* ... */
}

上記の Range を含め、以下の範囲型が使用できる。