簡體   English   中英

沒有類型參數的泛型類型上的泛型結構

[英]Generic struct over a generic type without type parameter

有可能在 Rust 中做這樣的事情嗎?

trait Foo<T> {}

struct A;
struct B;

struct Bar<T: Foo> {
    a: T<A>,
    b: T<B>
}

我知道我可以只為Bar使用兩個參數,但我認為必須有更好的方法來做到這一點。

我想實現一個Graph結構。 因為我不能只將節點和邊綁定到他們的父母生命周期,所以我想要像Rc這樣的東西。 但是,有時可能需要一個可以從多個線程訪問的Graph 所以我必須同時實現RcArc

這就是Foo的優點:我為RcArc實現了FooFoo需要Deref )並且我使用綁定到Foo的參數T 這就是我想要為單線程和多線程使用一個結構的方式。

⇒ 這目前無法在 Rust 的類型系統中表達 ☹

幸運的是,由於本 RFC 中提出的“通用關聯類型”,未來將成為可能。 您可以在相應的跟蹤問題中跟蹤實施和穩定狀態。


這里最重要的詞是“HKT”(H igherķinded牛逼YPES)。 這是尚未在 Rust 中實現的類型系統的一個特性。 Haskell 提供 HKT。 在 C++ 世界中,HKT 被稱為“模板模板”。 上面提到的泛型關聯類型也是 HKT 的一種形式。

但究竟什么是 HKT?

讓我們慢慢開始:我們所知道的簡單類型是什么? 讓我們列出一些類型: i32boolString 這些都是類型……您可以擁有這些類型的值(變量)。 Vec<i32>呢? 這也是一種簡單的類型! 你可以有一個Vec<i32>類型的變量,沒問題!

我們想將這些類型組合在一起; 我們稱這種分類為“某種類型”。 如果我們想以一種非常抽象的方式(關於類型的類型)進行討論,我們會選擇其他詞,在這種情況下是kind 甚至還有類型的符號。 對於上面的簡單類型,我們說:這些類型的種類是

*

是的,只是一顆星星,很容易。 符號在以后更有意義!


讓我們搜索與簡單類型不同的類型。 Mutex<HashMap<Vec<i32>, String>> ? 不,它可能相當復雜,但它仍然是那種*並且我們仍然可以擁有這種類型的變量。

Vec呢? 是的,我們省略了尖括號。 是的,這確實是另一種類型! 我們可以有一個Vec類型的變量嗎? 不! 什么的向量?!

這種捐贈方式為:

* -> *

這只是說:給我一個普通類型( * ),我將返回一個普通類型! 給這個東西( Vec )一個普通類型i32 ,它會返回一個普通類型Vec<i32> 它也稱為類型構造函數,因為它用於構造類型。 我們甚至可以更進一步:

* -> * -> *

這有點奇怪,因為它與柯里化有關,對於非 Haskell 程序員來說讀起來很奇怪。 但這意味着:給我兩種類型,我將返回一種類型。 讓我們考慮一個例子...... Result 在您提供兩個具體類型AB之后Result<A, B> Result類型構造函數將返回一個具體類型Result<A, B>

術語更高級的類型只是指所有不是*的類型,它們是類型構造函數。

在你的例子中

當您編寫struct Bar<T: Foo>您希望T* -> * ,這意味着:您可以給T一個類型並接收一個簡單類型。 但正如我所說,這在 Rust 中還不能表達。 要使用類似的語法,人們可能會想象這在未來可以工作:

// This does NOT WORK!
struct Bar<for<U> T> where T<U>: Foo {
    a: T<A>,
    b: T<B>,
}

for<>語法是從"higher-ranked trait bounds" (HRTB)借來的,它現在可用於對生命周期進行抽象(最常用於閉包)。

鏈接

如果您想閱讀有關此主題的更多信息,請訪問以下鏈接:


獎勵:萬一將實現關聯類型構造函數,您的問題的解決方案(我認為,因為沒有辦法測試)!

我們必須在我們的實現中繞道而行,因為 RFC 不允許將Rc作為類型參數直接傳遞。 可以這么說,它沒有直接介紹HKT。 但是正如 Niko 在他的博客文章中所說的那樣,我們可以通過使用所謂的“家族特征”,獲得與具有關聯類型構造函數的 HKT 相同的靈活性和功能。

/// This trait will be implemented for marker types, which serve as
/// kind of a proxy to get the real type.
trait RefCountedFamily {
    /// An associated type constructor. `Ptr` is a type constructor, because
    /// it is generic over another type (kind * -> *).
    type Ptr<T>;
}

struct RcFamily;
impl RefCountedFamily for RcFamily {
    /// In this implementation we say that the type constructor to construct
    /// the pointer type is `Rc`.
    type Ptr<T> = Rc<T>;
}

struct ArcFamily;
impl RefCountedFamily for ArcFamily {
    type Ptr<T> = Arc<T>;
}

struct Graph<P: RefCountedFamily> {
    // Here we use the type constructor to build our types
    nodes: P::Ptr<Node>,
    edges: P::Ptr<Edge>,
}

// Using the type is a bit awkward though:
type MultiThreadedGraph = Graph<ArcFamily>;

有關更多信息,您應該真正閱讀 Niko 的博客文章。 困難的話題解釋得足夠好,甚至我都能或多或少地理解它們!

編輯:我剛剛注意到 Niko 在他的博客文章中實際上使用了Arc / Rc示例! 我完全忘記了這一點,我自己想到了上面的代碼……但也許我的潛意識還記得,因為我和 Niko 一樣選擇了幾個名字。 無論如何,這是他(可能更好)對這個問題的看法

在某種程度上,Rust確實看起來很像 HKT(請參閱 Lukas 的回答以了解它們是什么),盡管有一些可以說是笨拙的語法。

首先,您需要為所需的指針類型定義接口,這可以使用泛型特征來完成。 例如:

trait SharedPointer<T>: Clone {
    fn new(v: T) -> Self;
    // more, eg: fn get(&self) -> &T;
}

加上一個泛型特性,它定義了一個關聯類型,它是你真正想要的類型,它必須實現你的接口:

trait Param<T> {
    type Pointer: SharedPointer<T>;
}

接下來,我們為我們感興趣的類型實現該接口:

impl<T> SharedPointer<T> for Rc<T> {
    fn new(v: T) -> Self {
        Rc::new(v)
    }
}
impl<T> SharedPointer<T> for Arc<T> {
    fn new(v: T) -> Self {
        Arc::new(v)
    }
}

並定義一些實現上述Param特征的虛擬類型。 這是關鍵部分; 我們可以有一個類型 ( RcParam ) 為任何T實現Param<T> ,包括能夠提供一個類型,這意味着我們正在模擬一個更高級的類型。

struct RcParam;
struct ArcParam;

impl<T> Param<T> for RcParam {
    type Pointer = Rc<T>;
}

impl<T> Param<T> for ArcParam {
    type Pointer = Arc<T>;
}

最后我們可以使用它:

struct A;
struct B;

struct Foo<P: Param<A> + Param<B>> {
    a: <P as Param<A>>::Pointer,
    b: <P as Param<B>>::Pointer,
}

impl<P: Param<A> + Param<B>> Foo<P> {
    fn new(a: A, b: B) -> Foo<P> {
        Foo {
            a: <P as Param<A>>::Pointer::new(a),
            b: <P as Param<B>>::Pointer::new(b),
        }
    }
}

fn main() {
    // Look ma, we're using a generic smart pointer type!
    let foo = Foo::<RcParam>::new(A, B);
    let afoo = Foo::<ArcParam>::new(A, B);
}

操場

暫無
暫無

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

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