コレクションはスマートポインタ
説明
コレクションに Deref
トレイトを使うことで、そのデータに対し所有や借用のビューを提供するスマートポインタのようにコレクションを扱えます。
例
use std::ops::Deref;
struct Vec<T> {
data: RawVec<T>,
//..
}
impl<T> Deref for Vec<T> {
type Target = [T];
fn deref(&self) -> &[T] {
//..
}
}
Vec<T>
はT
の所有のコレクションであり、対して、スライス(&[T]
)は T
の借用のコレクションです。Vec
に Deref
を実装することで、 &Vec<T>
から &[T]
への暗黙的な参照外しが可能になるとともに、自動参照外しの検索対象に組み込まれます。Vec
に実装されることが期待されるほとんどのメソッドは、代わりにスライスのそれとして実装されます。
String
と &str
もまた同様の関係にあります。
動機形成
所有権と借用はRust言語の重要な側面です。データ構造は、よりよいユーザエクスペリエンスのため、これらのセマンティクスに則る必要があります。データを所有するデータ構造を実装する場合、そのデータへの借用のビューを提供することで、よりフレキシブルなAPIを提供することが可能になります。
長所
ほとんどのメソッドが借用の型に対して実装可能です。それらメソッドが参照外しにより、所有の型に対しても暗黙的に利用可能になります。
データの借用を行うか、所有権を取るか、利用者が選択できるようにしましょう。
短所
参照外しを通してのみ利用可能なメソッドとトレイトは、境界チェックの際に考慮されません。そのため、このパターンを使用したデータ構造でのジェネリックプログラミングは複雑になる可能性があります (トレイト Borrow
や AsRef
など参照)。
議論
スマート・ポインタとコレクションは相似です。スマートポインタは単一のオブジェクトを指すのに対し、コレクションは多数のオブジェクトを指します。型システムの観点からは、両者にほとんど違いはありません。各データへアクセスする唯一の方法がコレクション経由であるなら、コレクションはそれらデータを所有しており、またそのコレクションはデータを削除する責任を持ちます(共有された所有権の場合でも、何らかの借用のビューは適切な場合があります)。コレクションがそのデータを所有する場合に、そのデータへの借用のビューを提供し複数回参照可能にすることは、一般に有用です。
ほとんどのスマートポインタ(例えば Foo<T>
)は Deref<Target=T>
を実装します。しかしコレクションは通常、カスタム型に参照外しされます。 [T]
と str
にはいくつかの言語サポートがありますが、一般的なケースでは、その必要はありません。Foo<T>
が Deref<Target=Bar<T>>
を実装してかまいません。ここで Bar
は動的なサイズの型であり、&Bar<T>
は Foo<T>
のデータの借用のビューです。
一般的に、順序付きコレクションは Range
に対して Index
を実装し、スライス構文を提供します。その対象は借用のビューになります。