1.16.1. 讓 Trait 泛型的兩種方式
Trait 能以兩種方式泛型化:
- 泛型型別參數。範例:
trait Foo<T> - 關聯型別。範例:
trait Foo { type Bar; }
兩者的差異在於:
- 使用關聯型別時,一個給定型別的 Trait 只有一種實作
- 使用泛型參數時,則可以有多種實作
簡單建議:如果可行,請優先使用關聯型別。
1.16.2. 泛型(型別參數)Trait
泛型 Trait 需要你指定所有泛型型別參數,並重複撰寫它們的邊界。
這在維護上較為困難。例如,若你為一個 Trait 新增泛型型別參數,則所有實作該 Trait 的型別都必須更新其程式碼。
這種形式也可能導致一個 Trait 對某個給定型別有多個實作。此時編譯器在推斷你實際想要哪個 Trait 實例時會遇到困難。有時你必須呼叫如 FromIterator::<u32>::from_iter 這類解決歧義的函式。
在某些情況下,這項功能也是一種優勢,例如:
-
impl PartialEq<BookFormat> for Book,其中BookFormat可以是不同型別 - 你可以同時實作
FromIterator<T>和FromIterator<&T> where T: Clone
1.16.3. 關聯型別 Trait
讓我們用一段程式碼作為範例:
trait Contains {
type A;
type B;
// Updates syntax to refer to these new types generically
fn contains(&self, _: &Self::A, _: &Self::B) -> bool;
}
Enter fullscreen mode Exit fullscreen mode
使用關聯型別時:
- 編譯器只需要知道實作該 Trait 的型別
- 邊界可以完全放在 Trait 本身,而不需要重複撰寫
- 未來新增另一個關聯型別不會影響使用者
- 具體型別會決定 Trait 內部的關聯型別,因此不需要使用解決歧義的函式。請看這個範例:
impl Contains for Container {
// Specify what types `A` and `B` are. If the `input` type
// is `Container(i32, i32)`, the `output` types are determined
// as `i32` and `i32`.
type A = i32;
type B = i32;
// `&Self::A` and `&Self::B` are also valid here.
fn contains(&self, number_1: &i32, number_2: &i32) -> bool {
(&self.0 == number_1) && (&self.1 == number_2)
}
// Grab the first number.
fn first(&self) -> i32 { self.0 }
// Grab the last number.
fn last(&self) -> i32 { self.1 }
}
Enter fullscreen mode Exit fullscreen mode
- 這個範例為
Container型別實作了ContainsTrait Container的Contains實作中的關聯型別,是由type A = i32;和type B = i32;所決定的
無法為多個目標型別實作 Deref
請看 Deref Trait 的原始碼:
pub trait Deref {
type Target: ?Sized;
fn deref(&self) -> &Self::Target;
}
Enter fullscreen mode Exit fullscreen mode
-
Target在type Target: ?Sized;中,就是我們所討論的目標型別
讓我們撰寫一個 Deref 實作來說明:
use std::ops::Deref;
struct Wrapper {
value: String,
}
impl Deref for Wrapper {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.value
}
}
Enter fullscreen mode Exit fullscreen mode
在上面的程式碼中,Wrapper 只能被解參考為 String。但如果你想讓 Wrapper 同時被解參考為 String 和 str,Rust 並不允許你再次實作 Deref,因為 Target 只能有一個具體型別。
換句話說,這段程式碼是非法的:
use std::ops::Deref;
struct Wrapper {
value: String,
}
// First Deref implementation, Target = String
impl Deref for Wrapper {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.value
}
}
// This is illegal: Rust does not allow a second `Deref` implementation
// for the same `Wrapper` type.
impl Deref for Wrapper {
type Target = str; // Conflict: Rust cannot infer which `Target` applies
fn deref(&self) -> &Self::Target {
&self.value
}
}
Enter fullscreen mode Exit fullscreen mode
無法使用多個 Item 來實作 Iterator Trait
原因與無法為多個目標型別實作 Deref 相同:這主要涉及關聯型別的唯一性以及Rust 編譯器的推斷規則。
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.