[英]What's the difference between a trait's generic type and a generic associated type?
在Rust中提供通用關聯類型之前會詢問此問題,盡管它們是提出並開發的 。
我的理解是,特質泛型和相關類型在它們可以綁定到結構的類型數量上有所不同。
泛型可以綁定任意數量的類型:
struct Struct;
trait Generic<G> {
fn generic(&self, generic: G);
}
impl<G> Generic<G> for Struct {
fn generic(&self, _: G) {}
}
fn main() {
Struct.generic(1);
Struct.generic("a");
}
關聯類型只綁定1種類型:
struct Struct;
trait Associated {
type Associated;
fn associated(&self, associated: Self::Associated);
}
impl Associated for Struct {
type Associated = u32;
fn associated(&self, _: Self::Associated) {}
}
fn main() {
Struct.associated(1);
// Struct.associated("a"); // `expected u32, found reference`
}
通用關聯類型是這兩者的混合。 它們綁定到一個類型正好相關的1個生成器,而這個生成器又可以關聯任意數量的類型。 那么前面例子中的Generic
與這個通用關聯類型有什么區別?
struct Struct;
trait GenericAssociated {
type GenericAssociated;
fn associated(&self, associated: Self::GenericAssociated);
}
impl<G> GenericAssociated for Struct {
type GenericAssociated = G;
fn associated(&self, _: Self::GenericAssociated) {}
}
通用關聯類型(GAT)是相關類型 ,它們本身是通用的 。 RFC以一個激勵性的例子開始,強調我的:
考慮以下特征作為代表性激勵示例:
trait StreamingIterator { type Item<'a>; fn next<'a>(&'a mut self) -> Option<Self::Item<'a>>; }
這個特性非常有用 - 它允許一種迭代器產生的值,這些值的壽命與傳遞給
next
的引用的生命周期相關。 該特征的一個特別明顯的用例是在向量上的迭代器,其在每次迭代時產生重疊的,可變的子序列。 使用標准的Iterator
接口,這樣的實現將是無效的,因為每個切片都需要與迭代器一樣長,而不是只要next
啟動的借用。這種特性不能用Rust表達 ,因為它依賴於一種更高級的多態性。 此RFC將擴展Rust以包括特定形式的高級多態性,這里將其稱為關聯類型構造函數。 此功能有許多應用程序,但主要應用程序與
StreamingIterator
特征的行相同:定義特征,這些特征產生的類型的生命周期與接收器類型的本地借用相關聯。
注意關聯類型Item
如何具有通用生命周期'a
。 RFC中的大多數示例都使用生命周期,但也有使用泛型類型的示例 :
trait PointerFamily { type Pointer<T>: Deref<Target = T>; fn new<T>(value: T) -> Self::Pointer<T>; }
注意關聯類型Pointer
如何具有泛型類型T
上一個示例中的
Generic
與此通用關聯類型之間的區別是什么
可能沒有,GAT的存在對你的情況沒有幫助,這似乎不需要一個本身就是通用的相關類型。
讓我們再看看你的最后一個例子(由我縮短):
trait GenericAssociated {
type GenericAssociated;
}
impl<G> GenericAssociated for Struct {
type GenericAssociated = G;
}
這不配備通用關聯類型! 您只是在impl
塊上使用泛型類型,並將其分配給關聯類型。 嗯,好的,我可以看到混亂的來源。
“類型參數G
不受impl trait,self type或謂詞約束”的示例錯誤。 實施GAT時,這不會改變,因為這與GAT無關。
在您的示例中使用GAT可能如下所示:
trait Associated {
type Associated<T>; // <-- note the `<T>`! The type itself is
// generic over another type!
// Here we can use our GAT with different concrete types
fn user_choosen<X>(&self, v: X) -> Self::Associated<X>;
fn fixed(&self, b: bool) -> Self::Associated<bool>;
}
impl Associated for Struct {
// When assigning a type, we can use that generic parameter `T`. So in fact,
// we are only assigning a type constructor.
type Associated<T> = Option<T>;
fn user_choosen<X>(&self, v: X) -> Self::Associated<X> {
Some(x)
}
fn fixed(&self, b: bool) -> Self::Associated<bool> {
Some(b)
}
}
fn main() {
Struct.user_choosen(1); // results in `Option<i32>`
Struct.user_choosen("a"); // results in `Option<&str>`
Struct.fixed(true); // results in `Option<bool>`
Struct.fixed(1); // error
}
但是回答你的主要問題:
特質的泛型類型和通用的相關類型之間有什么區別?
簡而言之: 它們允許延遲具體類型(或壽命)的應用,這使得整個類型系統更加強大。
RFC中有許多動機示例,最值得注意的是流迭代器和指針族示例。 讓我們快速了解為什么無法使用特征上的泛型實現流式迭代器。
流式迭代器的GAT版本如下所示:
trait Iterator {
type Item<'a>;
fn next(&self) -> Option<Self::Item<'_>>;
}
在當前的Rust中,我們可以將life參數放在trait而不是關聯的類型上:
trait Iterator<'a> {
type Item;
fn next(&'a self) -> Option<Self::Item>;
}
到目前為止一切順利:所有迭代器都可以像以前一樣實現這個特性。 但是如果我們想要使用呢?
fn count<I: Iterator<'???>>(it: I) -> usize {
let mut count = 0;
while let Some(_) = it.next() {
count += 1;
}
count
}
我們應該注釋什么生命周期? 除了注釋'static
生命周期”,我們有兩個選擇:
fn count<'a, I: Iterator<'a>>(it: I)
:這不起作用,因為調用者選擇函數的泛型類型。 但it
(它將next
調用中成為self
)存在於我們的堆棧框架中。 這意味着調用者不知道it
的生命周期。 因此我們得到了一個編譯器( Playground )。 這不是一個選擇。 fn count<I: for<'a> Iterator<'a>>(it: I)
(使用HRTB):這似乎有效,但它有微妙的問題。 現在我們要求I
在任何生命周期中實現Iterator
'a
。 這對於許多迭代器來說不是問題,但是一些迭代器會返回永遠不會生命的項目,因此它們無法在任何生命周期內實現Iterator
- 只比生命周期短。 使用這些排名較高的特征界限往往導致秘密的'static
邊界”,這是非常有限的。 所以這也並不總是有效。 正如你所看到的:我們無法正確地記下I
的界限。 實際上,我們甚至不想提及count
函數簽名中的生命周期! 它沒有必要。 這正是GAT允許我們做的事情(除其他事項外)。 通過GAT,我們可以寫:
fn count<I: Iterator>(it: I) { ... }
它會起作用。 因為“具體生命的應用”只在我們next
打電話時才會發生。
如果您對更多信息感興趣,可以查看我的博客文章“解決沒有GAT的廣義流式迭代器問題” ,我嘗試在特性上使用泛型類型來解決缺少GAT問題。 而且(劇透):它通常不起作用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.