精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Rust中GAT和高階類型

jf_wN0SrCdH ? 來源:Rust語言中文社區 ? 作者:Rust語言中文社區 ? 2022-11-07 10:21 ? 次閱讀

05ad3ac8-5de9-11ed-a3b6-dac502259ad0.jpg

Rust在類型系統級別上與Haskell,Scala有許多相似之處。

兩者都有類型(type),泛型類型(generic types),關聯類型(associated types)和特質/類型類(traits/type classes)(基本上是等效的)。

但是,Haskell有Rust缺乏的一個特性:高階類型(Higher Kinded Types), 也就是通常說的HKTs。

這不是Rust故意不添加,也不是Rust應該填補的一些差距。其實這是Rust的一個有意的設計。

結果就是,到沒有GATs出現時,某些代碼無法真正在Rust中實現。

以Haskell中的Functor為例。Functor其實是對類型上實現的map的一個更加抽象的實現,不關心具體類型的一個接口

比如,在Scala中,要實現一個map的Functor是這樣的:

importscala.language.higherKinds

traitFunctor[F[_]]{
defmap[A,B](fa:F[A])(f:A=>B):F[B]
}

如果你使用過Scala的集合api,這看起來應該非常相似。所有集合都可以使用map。

List(1,2,3).map(_+1)
//List(2,3,4)

你可能要問,什么是高階類型啊.你可以先理解為: 高階類型是類型的類型。這是什么意思呢? 我以Scala里面的代碼來說明一下。

scala>traitMyList[A]{
defhead:A
deftail:MyList[A]
}

然后用:k命令看下是什么類型:

scala>:k-vMyList
MyList'skindisF[A]
*->*
Thisisatypeconstructor:a1st-order-kindedtype.

MyList[String]類型是一個一階類型(1st-order-kinded type);任何MyList的類型都是由A參數化的,可以將MyList看作是一個類型函數,如A => MyList[A],因此給定一個類型A,我們可以創建一個新的類型MyList[A]。如果MyList是一階類型,那么什么是類型化類型呢?其實想想,什么是高階函數啊? 它不就是一個接受函數然后返回另外另一個函數的函數。同理可得,什么是高階類型呢? 它是由另一個類型參數化的類型。我再寫一個簡單的例子。

scala>traitFoo[F[_]]:k-vFoo
Foo'skindisX[F[A]]
(*->*)->*
Thisisatypeconstructorthattakestypeconstructor(s):ahigher-kindedtype.

這里的Foo就是一個高階類型。它是一個類型構造函數,參數也是一個類型構造函數。為了說清楚這點,我再寫一個例子.我寫一個trait,里面有一個leftFold的方法。

scala>traitFoldable[F[_]]{
|defleftFold[A,B](ob:F[A])(zero:B)(fn:(B,A)=>B):B
|}

然后呢,我再實現一個listFoldable, 這是最常見的吧。

scala>implicitvallistFoldable:Foldable[List]=newFoldable[List]{
|defleftFold[A,B](ob:List[A])(zero:B)(fn:(B,A)=>B):B={
|ob.foldLeft(zero)(fn)
|}
|}

上面我定義了一個適用于任何List類型的Foldable對象。leftFold方法將取A并使用A構造List[A]。

一種更好的寫法:

scala>importscala.language.implicitConversions
importscala.language.implicitConversions

scala>implicitclassListFoldableOpt[A](list:List[A])(implicitfold:Foldable[List]){
|defleftFold[B](zero:B)(fn:(B,A)=>B):B=
|fold.leftFold(list)(zero)(fn)
|}

scala>List(1,2,3).leftFold(0)(_+_)//6

這里先收一下,回到Rust中。

Rust中的很多不同結構都實現了map函數,比如: Option, Result, Iterator, and Future.

但是, 在Rust里面,還不能編寫可以給多種類型實現的通用Functortrait, 就像我剛才上面寫的trait Functor[F[_]]。通常呢 在Rust里面是單個類型單獨去實現map。例如,我這里自己寫了一個MyOption和MyResult枚舉類, 并實現了map方法.

#[derive(Debug,PartialEq)]
enumMyOption{
Some(A),
None,
}

implMyOption{
fnmapB,B>(self,f:F)->MyOption{
matchself{
MyOption::Some(a)=>MyOption::Some(f(a)),
MyOption::None=>MyOption::None,
}
}
}

#[test]
fntest_option_map(){
assert_eq!(MyOption::Some(5).map(|x|x+1),MyOption::Some(6));
assert_eq!(MyOption::None.map(|x:i32|x+1),MyOption::None);
}

#[derive(Debug,PartialEq)]
enumMyResult{
Ok(A),
Err(E),
}

implMyResult{
fnmapB,B>(self,f:F)->MyResult{
matchself{
MyResult::Ok(a)=>MyResult::Ok(f(a)),
MyResult::Err(e)=>MyResult::Err(e),
}
}
}

#[test]
fntest_result_map(){
assert_eq!(MyResult::Ok(5).map(|x|x+1),MyResult::(6));
assert_eq!(MyResult::Err("hello").map(|x:i32|x+1),MyResult::Err("hello"));
}

上面的幾個例子都是直接在自己的結構中定義的map方法。如果沒有GATs, 就不可能將map定義為一個trait的方法。這是為什么呢?下面是一個在Rust里面簡單的Functortrait實現,以及Option的實現.

traitNaiveFunctor{
typeT;
fnmap(self,f:F)->Self
where
F:FnMut(Self::T)->Self::T;
}

implNaiveFunctorforOption{
typeT=A;
fnmapA>(self,mutf:F)->Option{
matchself{
Some(a)=>Some(f(a)),
None=>None,
}
}
}

在上面的trait定義中,先給NaiveFunctor內部的值定義了一個關聯類型T。給Optionimpl這個trait,T=A。這就是問題所在。Rust將T硬編碼為一種類型A,因為通常在map函數中,希望返回的類型是B,但在之前版本穩定的Rust中,沒有辦法說"我想要一個既能與NaiveFunctor關聯的類型,又要求這個類型和關聯類型有一點不一樣。

這就是泛型關聯類型發揮作用的地方了。

多態Functor的實現

為了得到一個多態Functor.我想要的是:"我的類型最終應該是我在其中包裹的類型".例如,對于Option,我想說的是"如果我有一個Option,那么它肯定包含一個A類型,但如果它包含一個B類型,它將是Option"

這里就需要使用泛型關聯類型.

traitFunctor{
typeUnwrapped;
typeWrapped:Functor;
fnmap(self,f:F)->Self::Wrapped
where
F:FnMut(Self::Unwrapped)->B;
}

說明下上面的寫法:

每個Functor都有一個關聯的Unwrapped類型.

還有另一個關聯類型Wrapped,它與Self類似,但有一個不同的包裝值

和前面例子一樣, map接受兩個參數的一個是:self和一個函數

函數形參將從當前關聯的Unwrapped值映射到一個新的類型B

map的輸出是一個Wrapped

有點抽象。具體點現在看下Option類型是什么樣子的

implFunctorforOption{
typeUnwrapped=A;
typeWrapped=Option;

fnmapB,B>(self,mutf:F)->OptionwhereF:FnMut(A)->B{
matchself{
Some(x)=>Some(f(x)),
None=>None,
}
}
}

#[test]
fntest_option_map(){
assert_eq!(Some(5).map(|x|x+1),Some(6));
assert_eq!(None.map(|x:i32|x+1),None);
}

編譯通過,非常優雅。

類型類

在Haskell和Scala中,其實是不需要這種泛型關聯類型的。事實上,Haskell中Functors不使用任何關聯類型。Haskell中Functor的類型類遠遠早于其他語言中關聯類型的出現。為了進行比較,先看看它是什么樣子。

classFunctorfwhere
map::(a->b)->fa->fb

instanceFunctorOptionwhere
mapfoption=
caseoptionof
Somex->Some(fx)
None->None
traitFunctor[F[_]]{
defmap[A,B](fa:F[A])(f:A=>B):F[B]
}

把它轉換成Rust就是如下:

traitHktFunctor{
fnmapB>(self:Self,f:F)->Self;
}

implHktFunctorforOption{
fnmapB>(self:Option,f:F)->Option{
matchself{
Some(a)=>Some(f(a)),
None=>None,
}
}
}

但是上面的代碼是編譯不通過的。因為我試圖給Self提供類型參數。但是在Rust中, 單獨一個Option不是一個類型. Option要成為一個類型,必須有一個類型參數.比如: Option 是一個類型. Option卻不是.

相反,在Haskell中,Maybe Int是kind type的一種類型。Maybe是類型構造函數,類型為type -> type。但是出于創建類型類和實例的目的,可以將Maybe處理為具有自己的類型。Haskell中的Functor作用于type->type。

這就是我所說的"高階類型": 就是說我可以寫出擁有類型的類型(Kind)。

Pointer 的實現

Haskell中有一個有爭議的類型類叫做Pointed。之所以有爭議的,是因為它引入了一個類型類,但沒有與它相關的任何laws。我想在Rust中實現下Pointed。

什么是Pointed

Pointed的思想很簡單:將一個值包裝成一個類似Functor的東西。比如:在Option的情況下,它就用Some包裝它。在Result中,它使用Ok。對于Vec,它是一個單值向量。與Functor不同,這里是一個靜態方法,因為我們沒有現有的Point值要改。讓我們看看它在Rust中的實現。

traitPointed:Functor{
fnwrap(t:T)->Self::Wrapped;
}

implPointedforOption{
fnwrap(t:T)->Self::Wrapped{
Some(t)
}
}

這里需要注意的是: 我根本沒有在Option實現中使用A類型參數。

還有一點值得注意。調用wrap的結果返回的是Self::Wrapped。關于Self::Wrapped,到底是什么?從之前Functor的定義中,確切地知道一件事:Wrapped必須是一個Functor的。

本篇文章關于GATs就先聊到這里。感興趣的同學可以關注下公眾號。下篇再見。

審核編輯:湯梓紅

  • 函數
    +關注

    關注

    3

    文章

    4304

    瀏覽量

    62427
  • GAT
    GAT
    +關注

    關注

    0

    文章

    7

    瀏覽量

    6329
  • Rust
    +關注

    關注

    1

    文章

    228

    瀏覽量

    6570
收藏 人收藏

    評論

    相關推薦

    Rust的From和Into trait的基礎使用方法和進階用法

    、可靠和安全的系統級應用的首選語言。 Rust的From和Into是兩個重要的trait,它們可以幫助我們進行類型轉換。From trait允許我們從一個類型轉換到另一個
    的頭像 發表于 09-20 10:55 ?1724次閱讀

    如何在Rust讀寫文件

    見的內存安全問題和數據競爭問題。 在Rust,讀寫文件是一項非常常見的任務。本教程將介紹如何在Rust讀寫文件,包括基礎用法和進階用法。 基礎用法 讀取文件內容 使用 std::f
    的頭像 發表于 09-20 10:57 ?1975次閱讀

    Rust的多線程編程概念和使用方法

    Rust是一種強類型、高性能的系統編程語言,其官方文檔強調了Rust的標準庫具有良好的并發編程支持。Thread是Rust
    的頭像 發表于 09-20 11:15 ?925次閱讀

    Windows -編程-數據類型

    Windows -編程-數據類型Rust 的每個值都有特定的數據類型,它告訴 Rust 指定了什么樣的數據,以便它知道如何處理這些數據。我
    發表于 08-24 14:30

    RUST在嵌入式開發的應用是什么

    Rust是一種編程語言,它使用戶能夠構建可靠、高效的軟件,尤其是用于嵌入式開發的軟件。它的特點是:高性能:Rust具有驚人的速度和高內存利用率。可靠性:在編譯過程可以消除內存錯誤。生產效率:優秀
    發表于 12-24 08:34

    Rust代碼中加載靜態庫時,出現錯誤 ` rust-lld: error: undefined symbol: malloc `怎么解決?

    時,出現錯誤 ` [i]rust-lld: error: undefined symbol: malloc `。如何將這些定義包含在我的靜態庫
    發表于 06-09 08:44

    GAT 聯柵晶體管

    GAT 聯柵晶體
    發表于 11-06 17:07 ?722次閱讀

    聯柵晶體管(GAT)是什么意思?

    聯柵晶體管(GAT)是什么意思?  聯柵晶體管是一種新型功率開關半導體器件,簡稱GATGAT是介于雙極型晶體管(BJT)和場效應晶體管(FET)之間的特種器
    發表于 03-05 14:35 ?2801次閱讀

    Linux內核整合對 Rust 的支持

    Linux Plumbers Conference 2022 大會上舉行了一個 Rust 相關的小型會議,該會議討論的大方向大致為:正在進行的使 Rust 成為一種合適的系統編程語言的工作,以及在主線 Linux 內核整合對
    的頭像 發表于 09-19 11:06 ?1158次閱讀

    Rust原子類型和內存排序

    原子類型在構建無鎖數據結構,跨線程共享數據,線程間同步等多線程并發編程場景起到至關重要的作用。本文將從Rust提供的原子類型和原子類型的內
    的頭像 發表于 10-31 09:21 ?908次閱讀

    什么是高階類型啊?如何去定義高階類型

    這不是Rust故意不添加,也不是Rust應該填補的一些差距。 其實這是Rust的一個有意的設計。
    的頭像 發表于 11-07 10:20 ?1137次閱讀

    Rust的錯誤處理方法

    Rust 沒有提供類似于 Java、C++ 的 Exception 機制,而是使用 Result 枚舉的方式來實現。
    的頭像 發表于 02-20 09:37 ?913次閱讀

    rust語言基礎學習: Default trait

    Default trait 顧名思義是默認值,即Rust為特定類型實現 Default 特性時,可以為該類型賦予了可選的默認值。
    的頭像 發表于 05-22 16:16 ?1243次閱讀

    rust語言基礎學習: rust的錯誤處理

    錯誤是軟件不可避免的,所以 Rust 有一些處理出錯情況的特性。在許多情況下,Rust 要求你承認錯誤的可能性,并在你的代碼編譯前采取一些行動。
    的頭像 發表于 05-22 16:28 ?2052次閱讀

    RustPin/Unpin詳解

    對我來說,其中之一就是在Rust Pin/Unpin 。
    的頭像 發表于 07-20 11:00 ?830次閱讀