添字アクセスの舞台裏について。
以下は各種の添字アクセス。
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
添字式は Index
や IndexMut
トレイトを通して処理される。スライス型や配列型で添字式が使えるのは、これらを実装しているためである。なお、添字の型と出力の型はパラメトリックになっている。スライスと配列の場合、添字の型は 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
を含め、以下の範囲型が使用できる。
impl<T> SliceIndex<[T]> for Range<usize>
impl<T> SliceIndex<[T]> for RangeFrom<usize>
impl<T> SliceIndex<[T]> for RangeFull
impl<T> SliceIndex<[T]> for RangeInclusive<usize>
impl<T> SliceIndex<[T]> for RangeTo<usize>
impl<T> SliceIndex<[T]> for RangeToInclusive<usize>
impl<T> SliceIndex<[T]> for (Bound<usize>, Bound<usize>)