# 關聯類型
> [associated-types.md](https://github.com/rust-lang/rust/blob/master/src/doc/book/associated-types.md)
commit 6ba952020fbc91bad64be1ea0650bfba52e6aab4
關聯類型是Rust類型系統中非常強大的一部分。它涉及到‘類型族’的概念,換句話說,就是把多種類型歸于一類。這個描述可能比較抽象,所以讓我們深入研究一個例子。如果你想編寫一個`Graph`trait,你需要泛型化兩個類型:點類型和邊類型。所以你可能會像這樣寫一個trait,`Graph<N, E>`:
~~~
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。這里是定義:
~~~
trait Graph {
type N;
type E;
fn has_edge(&self, &Self::N, &Self::N) -> bool;
fn edges(&self, &Self::N) -> Vec<Self::E>;
}
~~~
十分簡單。關聯類型使用`type`關鍵字,并出現在trait體和函數中。
這些`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>;
}
~~~
### 實現關聯類型
就像任何 trait,使用關聯類型的 trait 用`impl`關鍵字來提供實現。下面是一個`Graph`的簡單實現:
~~~
# trait Graph {
# type N;
# type E;
# fn has_edge(&self, &Self::N, &Self::N) -> bool;
# fn edges(&self, &Self::N) -> Vec<Self::E>;
# }
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<Edge>`,不過它提供了如何實現這類 trait 的思路。首先我們需要3個`struct`,一個代表圖,一個代表點,還有一個代表邊。如果使用別的類型更合理,也可以那樣做,我們只是準備使用`struct`來代表這 3 個類型。
接下來是`impl`行,它就像其它任何 trait 的實現。
在這里,我們使用`=`來定義我們的關聯類型。trait 使用的名字出現在`=`的左邊,而我們`impl`的具體類型出現在右邊。最后,我們在函數聲明中使用具體類型。
### trait 對象和關聯類型
這里還有另外一個我們需要討論的語法:trait對象。如果你創建一個關聯類型的trait對象,像這樣:
~~~
# trait Graph {
# type N;
# type E;
# fn has_edge(&self, &Self::N, &Self::N) -> bool;
# fn edges(&self, &Self::N) -> Vec<Self::E>;
# }
# 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()
# }
# }
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;
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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;
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~
我們不能這樣創建一個trait對象,因為我們并不知道關聯的類型。相反,我們可以這樣寫:
~~~
# trait Graph {
# type N;
# type E;
# fn has_edge(&self, &Self::N, &Self::N) -> bool;
# fn edges(&self, &Self::N) -> Vec<Self::E>;
# }
# 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()
# }
# }
let graph = MyGraph;
let obj = Box::new(graph) as Box<Graph<N=Node, E=Edge>>;
~~~
`N=Node`語法允許我們提供一個具體類型,`Node`,作為`N`類型參數。`E=Edge`也是一樣。如果我們不提供這個限制,我們不能確定應該`impl`那個來匹配trait對象。
- 前言
- 貢獻者
- 1.介紹
- 2.準備
- 3.學習 Rust
- 3.1.猜猜看
- 3.2.哲學家就餐問題
- 3.3.其它語言中的 Rust
- 4.語法和語義
- 4.1.變量綁定
- 4.2.函數
- 4.3.原生類型
- 4.4.注釋
- 4.5.If語句
- 4.6.循環
- 4.7.所有權
- 4.8.引用和借用
- 4.9.生命周期
- 4.10.可變性
- 4.11.結構體
- 4.12.枚舉
- 4.13.匹配
- 4.14.模式
- 4.15.方法語法
- 4.16.Vectors
- 4.17.字符串
- 4.18.泛型
- 4.19.Traits
- 4.20.Drop
- 4.21.if let
- 4.22.trait 對象
- 4.23.閉包
- 4.24.通用函數調用語法
- 4.25.crate 和模塊
- 4.26.const和static
- 4.27.屬性
- 4.28.type別名
- 4.29.類型轉換
- 4.30.關聯類型
- 4.31.不定長類型
- 4.32.運算符和重載
- 4.33.Deref強制多態
- 4.34.宏
- 4.35.裸指針
- 4.36.不安全代碼
- 5.高效 Rust
- 5.1.棧和堆
- 5.2.測試
- 5.3.條件編譯
- 5.4.文檔
- 5.5.迭代器
- 5.6.并發
- 5.7.錯誤處理
- 5.8.選擇你的保證
- 5.9.外部函數接口
- 5.10.Borrow 和 AsRef
- 5.11.發布途徑
- 5.12.不使用標準庫
- 6.Rust 開發版
- 6.1.編譯器插件
- 6.2.內聯匯編
- 6.4.固有功能
- 6.5.語言項
- 6.6.鏈接進階
- 6.7.基準測試
- 6.8.裝箱語法和模式
- 6.9.切片模式
- 6.10.關聯常量
- 6.11.自定義內存分配器
- 7.詞匯表
- 8.語法索引
- 9.參考文獻
- 附錄:名詞中英文對照