トレイト Borrow
Since 1.0.0 (const: unstable) · Sourcepub trait Borrow<Borrowed> where Borrowed: ?Sized, { // Required method fn borrow(&self) -> &Borrowed; }説明を展開
データを借用するためのトレイト。
Rust では、異なる用途には異なる型の表現を与えるのが一般的である。例えば、特定の用途に適するよう値の保存場所や管理方法を
Box<T>やRc<T>のような型によって明確に選択できる。任意の型に使用できるこうした汎用的なラッパーに加え、いくつかの型は潜在的にコストのある別の切り口を提供している。そうした型の例にStringがあり、それは通常のstrに文字列を拡張する能力を付加している。これには単純な不変文字列には不要な追加情報を保持する必要がある。これらの型は背後にあるデータへのアクセスをそのデータの型への参照を通して提供する。これはその型「として借用される」と表現される。例えば、
Box<T>はTとして借用されStringはstrとして借用される。型は
Borrow<T>を実装し、そのトレイトのborrowメソッド内でTへの参照を提供する事により、特定の型Tとして借用できる事を表現する。一つの型を幾つかの異なる型として借用するのも自由である。もしその型として可変借用したい、つまり背後のデータの変更を許可したいなら、追加でBorrowMut<T>も実装できる。さらに、追加のトレイトの実装を提供している場合、それはそれらが背後にある型の代理としての動作の結果、背後にある型と同様の振舞いをすべきかどうかを考慮する必要がある。ジェネリックコードはこれらの追加のトレイト実装で同様の挙動が採用される際、通常は
Borrow<T>を使用する。こうしたトレイトは追加のトレイト境界としてよく現れる。特に、
Eq,OrdそしてHashは借用と所有の値が等価でなければならない:x.borrow() == y.borrow()はx == yと同じ結果を与えるべきである。もしジェネリックコードが単純に関係する型
Tへの参照を提供できる全ての型において機能する必要があるのなら、より多くの型を安全に実装できるためAsRef<T>を使用したほうが良い場合が多い。例
データのコレクションとして、
HashMap<K, V>はキーと値の両方を所有する。もしキーの実際のデータがある種の管理用の型にラップされていたとして、それでもまだキーデータへの参照を使った値の検索が可能であるべきである。例として、もしキーが文字列なら、ハッシュマップにはStringとして保存される事が多いだろうが、それは&strを使って検索できる。そのため、insertはStringでの操作が必要になるがgetは&strも使える必要がある。少し簡略化するが、
HashMap<K, V>の関連箇所は以下のように見える:use std::borrow::Borrow; use std::hash::Hash; pub struct HashMap<K, V> { // fields omitted } impl<K, V> HashMap<K, V> { pub fn insert(&self, key: K, value: V) -> Option<V> where K: Hash + Eq { // ... } pub fn get<Q>(&self, k: &Q) -> Option<&V> where K: Borrow<Q>, Q: Hash + Eq + ?Sized { // ... } }ハッシュマップ全体はキーの型
Kについてジェネリックである。これらのキーはハッシュマップに保存されるため、この型はキーのデータを所有する必要がある。キーと値のペアの挿入時、マップにはそうしたKが与えられ、正しいハッシュバケットを見つけ、そのキーがすでに存在するかをKに基づいて検査する。そのためK: Hash + Eqが要求される。とはいえ、マップ内で値を検索する際、検索のためのキーとして
Kへの参照を提供しなければならないとすると、そのような所有された値の作成が常に要求される。文字列キーでは、これはstrのみ利用可能な箇所で、検索のためだけにString値の作成が必要となる事を意味する。代わりに、
getメソッドはキーデータの背後の型、上記メソッドシグネチャではQと呼称、についてジェネリックである。それはKがQとして借用される事をK: Borrow<Q>を要求する事により宣言する。さらにQ: Hash + Eqを要求する事により、KとQがHashとEqトレイトの実装で同じ結果を生成する要件を示唆している。
getの実装は具体的なHashの実装が一致する事をあてにしていて、それはキーのハッシュバケットをQのHash::hashを呼び出す事により決定するのだが、そのキーはKの値から計算されたハッシュ値に基づいて挿入されている。結果として、もし
Qの値をラップしたKがQと異なるハッシュを生成するとハッシュマップは壊れてしまう。例として、文字列をラップするが比較では ASCII 文字の大文字小文字を無視する型があるとする:pub struct CaseInsensitiveString(String); impl PartialEq for CaseInsensitiveString { fn eq(&self, other: &Self) -> bool { self.0.eq_ignore_ascii_case(&other.0) } } impl Eq for CaseInsensitiveString { }二つの同じ値は同じハッシュ値を生成する必要があるため、
Hashの実装も ASCII の大文字小文字を無視する必要がある:impl Hash for CaseInsensitiveString { fn hash<H: Hasher>(&self, state: &mut H) { for c in self.0.as_bytes() { c.to_ascii_lowercase().hash(state) } } }
CaseInsensitiveStringはBorrow<str>を実装できるだろうか? 文字列スライスへの参照を提供する事はそれに含まれる所有された文字列を通して確かにできる。しかしそのHashの実装が異なるため、それはstrと異なる挙動をする、ゆえに実際にはBorrow<str>を実装してはならない。もし、基盤となるstrへのアクセスを他者に許可したいのなら、追加の要件を伴わないAsRef<str>を通してそれを行う事はできる。要求されるメソッド
1.0.0 · Source
fn borrow(&self) -> &Borrowed所有された値から不変借用をする。
例
use std::borrow::Borrow; fn check<T: Borrow<str>>(s: T) { assert_eq!("Hello", s.borrow()); } let s = "Hello".to_string(); check(s); let s = "Hello"; check(s);