關聯類型是Rust類型系統中十分強力的一部分。它涉及到‘類型族’的概念,換句話說,就是把多種類型歸于一類。這個描述可能比較抽象,所以讓我們深入研究一個例子。如果你想編寫一個`Graph`特性,你需要泛型化兩個類型:點類型和邊類型。所以你可能會像這樣寫一個特性,`Graph`:
~~~
trait Graph<N, E> {
fn has_edge(&self, &N, &N) -> bool;
fn edges(&self, &N) -> Vec<E>;
// etc
}
~~~
雖然這可以工作,不過顯得很尷尬,例如,任何需要一個`Graph`作為參數的函數都需要泛型化的`N'ode和`E'dge類型:
~~~
fn distance<N, E, G: Graph<N, E>>(graph: &G, start: &N, end: &N) -> u32 { ... }
~~~
我們的距離計算并不需要`Edge`類型,所以函數簽名中`E`只是寫著玩的。
我們需要的是對于每一種`Graph`類型,都使用一個特定的的`N'ode和`E'dge類型。我們可以用關聯類型來做到這一點:
~~~
trait Graph {
type N;
type E;
fn has_edge(&self, &Self::N, &Self::N) -> bool;
fn edges(&self, &Self::N) -> Vec<Self::E>;
// etc
}
~~~
現在,我們使用一個抽象的`Graph`了:
~~~
fn distance<G: Graph>(graph: &G, start: &G::N, end: &G::N) -> uint { ... }
~~~
這里不再需要處理`E'dge類型了。
讓我們更詳細的回顧一下。
## 定義關聯類型
讓我們構建一個`Graph`特性。這里是定義:
~~~
trait Graph {
type N;
type E;
fn has_edge(&self, &Self::N, &Self::N) -> bool;
fn edges(&self, &Self::N) -> Vec<Self::E>;
}
~~~
十分簡單。關聯類型使用`type`關鍵字,并出現在特性體和函數中。
這些`type`聲明跟函數定義一樣。例如,如果我們想`N`類型實現`Display`,這樣我們就可以打印出點類型,我們可以這樣寫:
~~~
use std::fmt;
trait Graph {
type N: fmt::Display;
type E;
fn has_edge(&self, &Self::N, &Self::N) -> bool;
fn edges(&self, &Self::N) -> Vec<Self::E>;
}
~~~
## 實現關聯類型
就像任何特性,使用關聯類型的特性用`impl`關鍵字來提供實現。下面是一個`Graph`的簡單實現:
~~~
struct Node;
struct Edge;
struct MyGraph;
impl Graph for MyGraph {
type N = Node;
type E = Edge;
fn has_edge(&self, n1: &Node, n2: &Node) -> bool {
true
}
fn edges(&self, n: &Node) -> Vec<Edge> {
Vec::new()
}
}
~~~
這個可笑的實現總是返回`true`和一個空的`Vec`,不過它提供了如何實現這類特性的思路。首先我們需要3個`struct`,一個代表圖,一個代表點,還有一個代表邊。如果使用別的類型更合理,也可以那樣做,我們只是準備使用`struct`來代表這3個類型。
接下來是`impl`行,它就像其它任何特性的實現。
在這里,我們使用`=`來定義我們的關聯類型。特性使用的名字出現在`=`的左邊,而我們`impl`的具體類型出現在右邊。最后,我們在函數聲明中使用具體類型。
## 特性對象和關聯類型
這里還有另外一個我們需要討論的語法:特性對象。如果你創建一個關聯類型的特性對象,像這樣:
~~~
let graph = MyGraph;
let obj = Box::new(graph) as Box<Graph>;
~~~
你會得到兩個錯誤:
~~~
error: the value of the associated type `E` (from the trait `main::Graph`) must
be specified [E0191]
let obj = Box::new(graph) as Box<Graph>;
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
24:44 error: the value of the associated type `N` (from the trait
`main::Graph`) must be specified [E0191]
let obj = Box::new(graph) as Box;
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~
我們不能這樣創建一個特性對象,因為我們并不知道關聯的類型。相反,我們可以這樣寫:
~~~
let graph = MyGraph;
let obj = Box::new(graph) as Box<Graph<N=Node, E=Edge>>;
~~~
`N=Node`語法允許我們提供一個具體類型,`Node`,作為`N`類型參數。`E=Edge`也是一樣。如果我們不提供這個限制,我們不能確定應該`impl`那個來匹配特性對象。
- 前言
- 1.介紹
- 2.準備
- 2.1.安裝Rust
- 2.2.Hello, world!
- 2.3.Hello, Cargo!
- 3.學習Rust
- 3.1.猜猜看
- 3.2.哲學家就餐問題
- 3.3.其它語言中的Rust
- 4.高效Rust
- 4.1.棧和堆
- 4.2.測試
- 4.3.條件編譯
- 4.4.文檔
- 4.5.迭代器
- 4.6.并發
- 4.7.錯誤處理
- 4.8.外部語言接口
- 4.9.Borrow 和 AsRef
- 4.10.發布途徑
- 5.語法和語義
- 5.1.變量綁定
- 5.2.函數
- 5.3.原生類型
- 5.4.注釋
- 5.5.If語句
- 5.6.for循環
- 5.7.while循環
- 5.8.所有權
- 5.9.引用和借用
- 5.10.生命周期
- 5.11.可變性
- 5.12.結構體
- 5.13.枚舉
- 5.14.匹配
- 5.15.模式
- 5.16.方法語法
- 5.17.Vectors
- 5.18.字符串
- 5.19.泛型
- 5.20.Traits
- 5.21.Drop
- 5.22.if let
- 5.23.trait對象
- 5.24.閉包
- 5.25.通用函數調用語法
- 5.26.包裝箱和模塊
- 5.27.`const`和`static`
- 5.28.屬性
- 5.29.`type`別名
- 5.30.類型轉換
- 5.31.關聯類型
- 5.32.不定長類型
- 5.33.運算符和重載
- 5.34.`Deref`強制多態
- 5.35.宏
- 5.36.裸指針
- 6.Rust開發版
- 6.1.編譯器插件
- 6.2.內聯匯編
- 6.3.不使用標準庫
- 6.4.固有功能
- 6.5.語言項
- 6.6.鏈接參數
- 6.7.基準測試
- 6.8.裝箱語法和模式
- 6.9.切片模式
- 6.10.關聯常量
- 7.詞匯表
- 8.學院派研究
- 勘誤