サーチ…
前書き
形質は、 struct
が実装しなければならない「契約」を記述する方法です。形質は通常、メソッドシグネチャを定義しますが、 特性境界がこれを可能にするならば、 特性の他のメソッドに基づいて実装を提供することもできます。
オブジェクト指向プログラミングに精通している人にとっては、特性は微妙な違いを持つインターフェースと考えることができます。
構文
- 特性{fnメソッド(...) - > ReturnType; ...}
- trait:バインドされた{fnメソッド(...) - > ReturnType; ...}
- インプットTrait for Type {fn method(...) - > ReturnType {...} ...}
- impl <T> Trait for Tここで、T:Bounds {fn method(...) - > ReturnType {...} ...}
備考
- 形質は一般的にインターフェースに似ていますが、その2つを区別することが重要です。 Javaのようなオブジェクト指向言語では、インタフェースはそれらを拡張するクラスの不可欠な部分です。 Rustでは、これらの特性が使われない限り、コンパイラは構造体の特性を何も知らない。
基本
特性の作成
trait Speak {
fn speak(&self) -> String;
}
特性の実装
struct Person;
struct Dog;
impl Speak for Person {
fn speak(&self) -> String {
String::from("Hello.")
}
}
impl Speak for Dog {
fn speak(&self) -> String {
String::from("Woof.")
}
}
fn main() {
let person = Person {};
let dog = Dog {};
println!("The person says {}", person.speak());
println!("The dog says {}", dog.speak());
}
静的および動的ディスパッチ
特定の特性を実装するオブジェクトを受け入れる関数を作成することは可能です。
スタティックディスパッチ
fn generic_speak<T: Speak>(speaker: &T) {
println!("{0}", speaker.speak());
}
fn main() {
let person = Person {};
let dog = Dog {};
generic_speak(&person);
generic_speak(&dog);
}
静的ディスパッチはここで使用されます。つまり、Rustコンパイラは、 Dog
型とPerson
型の両方に対してgeneric_speak
関数の特殊バージョンを生成します。コンパイル中の多形関数(または多形体)の特別なバージョンの生成は、 単形化と呼ばれます。
ダイナミックディスパッチ
fn generic_speak(speaker: &Speak) {
println!("{0}", speaker.speak());
}
fn main() {
let person = Person {};
let dog = Dog {};
generic_speak(&person as &Speak);
generic_speak(&dog); // gets automatically coerced to &Speak
}
ここでは、コンパイルされたバイナリにgeneric_speak
バージョンが1つだけ存在し、実行時にvtableの参照を使用してspeak()
呼び出されます。したがって、動的ディスパッチを使用すると、コンパイルされたバイナリのコンパイルとサイズの縮小が速くなりますが、実行時にはやや遅くなります。
&Speak
またはBox<Speak>
オブジェクトは、 traitオブジェクトと呼ばれます 。
関連タイプ
- 特性を実装する型と関連型との間に1対1の関係がある場合、関連型を使用します。
- これは出力型とも呼ばれます。これは、型を特性を適用するときに型に与えられる項目なので、 出力型とも呼ばれます。
創造
trait GetItems {
type First;
// ^~~~ defines an associated type.
type Last: ?Sized;
// ^~~~~~~~ associated types may be constrained by traits as well
fn first_item(&self) -> &Self::First;
// ^~~~~~~~~~~ use `Self::` to refer to the associated type
fn last_item(&self) -> &Self::Last;
// ^~~~~~~~~~ associated types can be used as function output...
fn set_first_item(&mut self, item: Self::First);
// ^~~~~~~~~~~ ... input, and anywhere.
}
導入
impl<T, U: ?Sized> GetItems for (T, U) {
type First = T;
type Last = U;
// ^~~ assign the associated types
fn first_item(&self) -> &Self::First { &self.0 }
fn last_item(&self) -> &Self::Last { &self.1 }
fn set_first_item(&mut self, item: Self::First) { self.0 = item; }
}
impl<T> GetItems for [T; 3] {
type First = T;
type Last = T;
fn first_item(&self) -> &T { &self[0] }
// ^ you could refer to the actual type instead of `Self::First`
fn last_item(&self) -> &T { &self[2] }
fn set_first_item(&mut self, item: T) { self[0] = item; }
}
関連する型の参照
型T
がGetItems
をジェネリックで実装していると確信できたら、単にT::First
を使って関連する型を得ることができます。
fn get_first_and_last<T: GetItems>(obj: &T) -> (&T::First, &T::Last) {
// ^~~~~~~~ refer to an associated type
(obj.first_item(), obj.last_item())
}
それ以外の場合は、型が実装している特性をコンパイラに明示的に伝える必要があります
let array: [u32; 3] = [1, 2, 3];
let first: &<[u32; 3] as GetItems>::First = array.first_item();
// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [u32; 3] may implement multiple traits which many
// of them provide the `First` associated type.
// thus the explicit "cast" is necessary here.
assert_eq!(*first, 1);
関連する型の制約
fn clone_first_and_last<T: GetItems>(obj: &T) -> (T::First, T::Last)
where T::First: Clone, T::Last: Clone
// ^~~~~ use the `where` clause to constraint associated types by traits
{
(obj.first_item().clone(), obj.last_item().clone())
}
fn get_first_u32<T: GetItems<First=u32>>(obj: &T) -> u32 {
// ^~~~~~~~~~~ constraint associated types by equality
*obj.first_item()
}
デフォルトのメソッド
trait Speak {
fn speak(&self) -> String {
String::from("Hi.")
}
}
このメソッドは、 impl
ブロックで上書きされる場合を除いて、デフォルトで呼び出されます。
struct Human;
struct Cat;
impl Speak for Human {}
impl Speak for Cat {
fn speak(&self) -> String {
String::from("Meow.")
}
}
fn main() {
let human = Human {};
let cat = Cat {};
println!("The human says {}", human.speak());
println!("The cat says {}", cat.speak());
}
出力:
人間はこんにちはと言います。
猫はヤングを言う。
形質に限界を置く
新しい特性を定義する場合、この特性を実装したいタイプがいくつかの制約または境界を検証することを強制することが可能である。
標準ライブラリから例に取ると、 DerefMut
特性は、型が最初にその兄弟実装する必要Deref
形質を:
pub trait DerefMut: Deref {
fn deref_mut(&mut self) -> &mut Self::Target;
}
これは、順番に、可能DerefMut
関連付けられている型を使用してTarget
によって定義されDeref
。
構文は継承を思い起こさせるかもしれませんが、
- それは、束縛された形質のすべての関連する項目(定数、型、関数、...)をもたらします
- それから多型を可能に
&DerefMut
ために&Deref
これは本質的に異なります:
- (
'static
)ライフタイムをバインドとして使用することは可能です - 束縛された特質項目を無効にすることはできません(機能さえも)
したがって、それを別の概念と考えることが最善です。
複数のバインドされたオブジェクト型
Static Dispatch関数に複数のオブジェクト型を追加することもできます。
fn mammal_speak<T: Person + Dog>(mammal: &T) {
println!("{0}", mammal.speak());
}
fn main() {
let person = Person {};
let dog = Dog {};
mammal_speak(&person);
mammal_speak(&dog);
}