簡體   English   中英

依賴於 Rust 中另一個泛型的泛型類型

[英]Generic types that depend on another generic in Rust

我正在嘗試創建一個通用的結構,該結構具有通用實現特征的界限。 該特征本身是通用的。 這是在 Rust 1.49.0 中。

如果我這樣做:

trait Foo<T> {}

struct Baz<F: Foo<T>> {
    x: F,
}

我得到一個編譯錯誤,因為T沒有定義。 但如果我定義它:

trait Foo<T> {}

struct Baz<T, F: Foo<T>> {
    x: F,
}

然后我得到一個編譯器錯誤,因為T未使用。

唯一的選擇似乎是包含一個PhantomData<T>字段,但如果我的通用依賴變得更加復雜,這開始變得更加笨拙:

use std::marker::PhantomData;

trait Foo<T> {}

struct Baz<T, U, F: Foo<T>, G: Foo<U>> {
    phantom_t: PhantomData<T>,
    phantom_u: PhantomData<U>,
    x: F,
    y: G,
}

我的一半領域是幻影。 該結構實際上是鬧鬼的。

我的問題是:最后編譯的示例真的是慣用的 Rust 嗎? 如果是這樣,為什么 Rust不能檢測到 Baz< T Baz<T, Foo<T>>中的 T 實際使用?

最后編譯的例子真的是慣用的Rust嗎?

存儲多個幻像類型參數的慣用方法是使用元組:

struct Baz<T, U, F: Foo<T>, G: Foo<U>> {
    x: F,
    y: G,
    _t: PhantomData<(T, U)>,
}

為什么 Rust 檢測不到 Baz< T Baz<T, Foo<T>>中的 T 實際被使用?

由於方差drop check ,這實際上是預期的行為。 這里的想法是編譯器需要知道它可以對類型參數T施加什么約束,並且PhantomData類型的使用將指導編譯器如何做到這一點。

您可以了解更多關於PhantomData以及它如何影響Rust nomicon中的方差的信息。

您可能應該做以下事情之一:

  1. 如果每個 struct F可以通過多種方式實現FooFoo<String>Foo<i32>等,對於相同的F ),只需從 struct Baz中刪除邊界。 T的具體選擇在您決定使用它之前是不相關的,但是附加一個邊界會迫使您選擇一個T結構將對其起作用,即使F可以與多種類型一起使用。

    相反,將<T>參數和F: Foo<T>綁定僅放在使用Timpl塊上(請參閱是否應該在 struct 和 impl 中重復 trait bounds? )。

  2. 如果每個 struct F只希望以一種方式實現Foo ,則使T成為Foo的關聯類型而不是泛型類型。 請參閱何時適合使用關聯類型與泛型類型?

可能不應該使用PhantomData<T> 這種標記類型不僅用於方差,正如 Ibraheem 的回答正確提到的那樣,而且還由drop 檢查器確定Baz<T>邏輯上是否包含T ,並由編譯器確定應該為Baz<T>實現哪些自動特征Baz<T> 如果您不小心如何使用它,您可能會以微妙的方式意外地過度約束或約束 API(請參閱讓結構體比賦予該結構體的方法的參數更長壽)。 更糟糕的是,由於類型的這些屬性在外部可見,因此PhantomData的類型參數的選擇可能會暴露給外部代碼,這使得修復任何錯誤都成為重大更改。

如果您不知道PhantomData<T>PhantomData<fn(T)>PhantomData<fn() -> T>PhantomData<fn(T) -> T>PhantomData<*const T>還是PhantomData<*mut T>使用(六個有意義的不同的東西,這個列表並不詳盡),你應該通讀上面的鏈接並嘗試確定Baz應該對T有哪種方差、丟棄行為和自動特征行為. 這些是了解通用結構的重要品質,它們是其外部 API 的一部分。 如果其中任何一個對您的用例沒有意義,則可能意味着Baz根本不應該是通用的T

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM