Rust libstd内での特殊化の使用例 (1.23.0時点)
概要: Rust libstd内では既に特殊化が使用されているので、特定の条件を満たすことでより効率なコードが生成される。
PartialEq<[T]>
- 最適化される処理: スライスの比較処理
- 間接的に最適化される処理:
Vec<T>
,str
,String
の比較など
- 間接的に最適化される処理:
- 条件: 内部トレイト
BytewiseEquality
が実装されている場合。具体的には要素型がu8
,i8
,u16
,i16
,u32
,i32
,u64
,i64
,usize
,isize
,char
,bool
の場合 - 理由:
memcmp
で効率的に比較できるため。 - ディスパッチ用トレイト: 内部トレイト
SlicePartialEq
Vec<T>::from_iter
, Vec<T>::extend
(ムーブ)
- 最適化される処理:
T
のイテレータからのVec<T>
の生成と延長- 間接的に最適化される処理:
iter.collect::<Vec<_>>()
など
- 間接的に最適化される処理:
- 条件1: unstableかつunsafeなトレイト
TrustedLen
が実装されている場合。これはsize_hint
が正確な長さを与えることをunsafeレベルで保証するトレイトである。 (ExactSizeIterator
はsafeであり、契約の内容も微妙に異なる)[T]
,Option<T>
,Result<T, E>
由来のイテレータとstr.bytes()
,iter::empty()
,iter::once()
,(n..m)
,(n..=m)
はTrustedLen
である。rev()
,cloned()
,chain()
,zip()
,map()
,enumerate()
はTrustedLen
を保つ。
- 理由1: 通常の生成/延長処理では、容量を気にしながら適宜再確保をする必要があるが、イテレータの長さが判明している場合は、最初に再確保したあとはループの各ステップでは容量チェックを省略できるから。
- 条件2: イテレータが
vec.into_iter()
自体だった場合 - 理由2: 当該イテレータが一度も消費されていない場合、単にもとの
vec
を復元するだけでよいから。 - ディスパッチ用トレイト: 内部トレイト
SpecExtend
Vec<T>::from_iter
, Vec<T>::extend
(参照)
- 最適化される処理:
&T
のイテレータからのVec<T>
の延長 (T: Copy
のとき可能) - 条件: スライスの
slice.iter()
に由来するとき - 理由:
memcpy
で効率よくコピー可能で、再確保も高々1回でよく、パニックガードが要らないから。 - ディスパッチ用トレイト: 内部トレイト
SpecExtend
(上と同じ)
BinaryHeap<T>::extend
, LinkedList<T>::extend
- 最適化される処理: イテレータからの
BinaryHeap<T>
の延長とLinkedList<T>
の延長- 間接的に最適化される処理:
iter.collect::<LinkedList<_>>()
など
- 間接的に最適化される処理:
- 条件1:
BinaryHeap<T>
をBinaryHeap<T>
で延長する場合 - 理由1: swapして逆向きに追加したほうが速い場合や、ヒープの再ビルドをしたほうが速い場合がある。
- 条件2:
LinkedList<T>
をLinkedList<T>
で延長する場合 - 理由2: 単に連結すればよい。
- ディスパッチ用トレイト: 内部トレイト
SpecExtend
vec![x; n]
- 最適化される処理:
vec![x; n]
(内部的には隠し関数alloc::vec::from_elem
) - 条件1: 要素型が
u8
- 理由1:
memset
で効率的に塗れるため。 - 条件2: 要素型が
u8
,i8
,u16
,i16
,u32
,i32
,u64
,i64
,u128
,i128
,usize
,isize
,f32
,f64
で値が0のとき - 理由2:
memset
で効率的に塗れるため。 - ディスパッチ用トレイト: 内部トレイト
SpecFromElem
ToString
- 最適化される処理:
to_string
による文字列化 (一般にはDisplay
に対して実装される) - 条件:
str
,Cow<'a, str>
,String
のいずれかの場合 - 理由: 文字列をコピーするだけだから。
- ディスパッチ用トレイト: なし (
ToString
を直接特殊化)
iter.zip
- 最適化される処理:
iter.zip()
で得られるイテレータの実装 - 条件: 内部トレイト
TrustedRandomAccess
が実装されている場合。これは名前の通りインデックスを引数としてアクセスする処理を実装し、それが本来のイテレータの動作と一致することを保証する。ただし、実際にランダムアクセスのために使われることはない。slice.iter()
,slice.iter_mut()
,str.bytes()
はTrustedRandomAccess
である。cloned()
,zip()
,map()
,enumerate()
,fuse()
はTrustedRandomAccess
を保つ。
- 理由: この特殊化により、
slice1.iter().zip(slice2.iter())
のような場合に単一のインデックス変数でループするコードが生成され、効率が良くなるらしい。 - ディスパッチ用トレイト: 内部トレイト
ZipImpl
iter.fuse
- 最適化される処理:
iter.fuse()
で得られるイテレータの実装 - 条件: unstableなトレイト
FusedIterator
が実装されている場合。つまり、元のイテレータが既に、「None
を出力したら以降はずっとNone
」という性質を満たしているとき。 - 理由: 既にfusedなので、
self.done
フラグをチェックする必要がなくなる。 - ディスパッチ用トレイト: なし (
Iterator
,DoubleEndedIterator
を直接特殊化)
Arc<[T]>::from
, Rc<[T]>::from
- 最適化される処理:
&[T]
からArc<[T]>
,Rc<[T]>
を作る処理 (一般にはT: Clone
なら使える) - 条件:
T: Copy
のとき - 理由:
memcpy
で効率的に複製できるし、clone
と違ってpanicしないのでパニックガードが要らない。 - ディスパッチ用トレイト:
ArcFromSlice
,RcFromSlice