偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

在 Rust 編程中使用泛型

開(kāi)發(fā)
本文的內(nèi)容將涉及泛型定義函數(shù)、結(jié)構(gòu)體、枚舉和方法, 還將討論泛型如何影響代碼性能。

1.摘要

Rust中的泛型可以讓我們?yōu)橄窈瘮?shù)簽名或結(jié)構(gòu)體這樣的項(xiàng)創(chuàng)建定義, 這樣它們就可以用于多種不同的具體數(shù)據(jù)類(lèi)型。下面的內(nèi)容將涉及泛型定義函數(shù)、結(jié)構(gòu)體、枚舉和方法, 還將討論泛型如何影響代碼性能。

2.在函數(shù)定義中使用泛型

當(dāng)使用泛型定義函數(shù)時(shí),本來(lái)在函數(shù)簽名中指定參數(shù)和返回值的類(lèi)型的地方,會(huì)改用泛型來(lái)表示。采用這種技術(shù),使得代碼適應(yīng)性更強(qiáng),從而為函數(shù)的調(diào)用者提供更多的功能,同時(shí)也避免了代碼的重復(fù)。

看下面的代碼例子, 定義了兩個(gè)函數(shù), 功能都差不多,作用是分別尋找slice中最大的i32和slice中最大的char, 只是數(shù)據(jù)類(lèi)型不同。

fn largest_i32(list: &[i32]) -> &i32 {
    let mut largest = &list[0];

    for item in list {
        if item > largest {
            largest = item;
        }
    }

    largest
}

fn largest_char(list: &[char]) -> &char {
    let mut largest = &list[0];

    for item in list {
        if item > largest {
            largest = item;
        }
    }

    largest
}

fn main() {
    let number_list = vec![34, 50, 25, 100, 65];

    let result = largest_i32(&number_list);
    println!("The largest number is {}", result);

    let char_list = vec!['y', 'm', 'a', 'q'];

    let result = largest_char(&char_list);
    println!("The largest char is {}", result);
}

編譯一下代碼, 輸出如下:

我們現(xiàn)在需要定義一個(gè)新函數(shù), 引進(jìn)泛型參數(shù)來(lái)消除這種因數(shù)據(jù)類(lèi)型不同而導(dǎo)致的函數(shù)重復(fù)定義。為了參數(shù)化這個(gè)新函數(shù)中的這些類(lèi)型,我們需要為類(lèi)型參數(shù)命名,道理和給函數(shù)的形參起名一樣。任何標(biāo)識(shí)符都可以作為類(lèi)型參數(shù)的名字。這里選用 T,因?yàn)閭鹘y(tǒng)上來(lái)說(shuō),Rust 的類(lèi)型參數(shù)名字都比較短,通常僅為一個(gè)字母,同時(shí),Rust 類(lèi)型名的命名規(guī)范是首字母大寫(xiě)駝峰式命名法(UpperCamelCase)。T 作為 “type” 的縮寫(xiě)是大部分 Rust 程序員的首選。

如果要在函數(shù)體中使用參數(shù),就必須在函數(shù)簽名中聲明它的名字,好讓編譯器知道這個(gè)名字指代的是什么。同理,當(dāng)在函數(shù)簽名中使用一個(gè)類(lèi)型參數(shù)時(shí),必須在使用它之前就聲明它。為了定義泛型版本的 largest 函數(shù),類(lèi)型參數(shù)聲明位于函數(shù)名稱(chēng)與參數(shù)列表中間的尖括號(hào) <> 中,像這樣:

fn largest<T>(list: &[T]) -> &T

可以這樣理解這個(gè)定義:函數(shù) largest 有泛型類(lèi)型 T。它有個(gè)參數(shù) list,其類(lèi)型是元素為 T 的 slice。largest 函數(shù)會(huì)返回一個(gè)與 T 相同類(lèi)型的引用。

按照這個(gè)思想, 我們將代碼改造如下:

fn largest<T>(list: &[T]) -> &T {
    let mut largest = &list[0];

    for item in list {
        if item > largest {
            largest = item;
        }
    }

    largest
}

fn main() {
    let number_list = vec![34, 50, 25, 100, 65];

    let result = largest(&number_list);
    println!("The largest number is {}", result);

    let char_list = vec!['y', 'm', 'a', 'q'];

    let result = largest(&char_list);
    println!("The largest char is {}", result);
}

一切似乎很順利, 嘗試編譯這段代碼, 編譯器結(jié)果如下:

這次編譯沒(méi)有通過(guò)的原因Rust編譯器用綠色標(biāo)識(shí)出來(lái)了, 缺少一個(gè): std:cmp::PartialOrd, 先暫且認(rèn)為這個(gè)是Rust標(biāo)準(zhǔn)庫(kù)要求的東西, 加上重新編譯一下試試:

fn largest<T: std::cmp::PartialOrd>(list: &[T]) -> &T {
    let mut largest = &list[0];

    for item in list {
        if item > largest {
            largest = item;
        }
    }

    largest
}

重新編譯結(jié)果如下:

我們?cè)诖a中下了一個(gè)斷點(diǎn), 能夠執(zhí)行到此處說(shuō)明代碼已經(jīng)沒(méi)有問(wèn)題。實(shí)際上上面這個(gè)錯(cuò)誤表明 largest 的函數(shù)體不能適用于 T 的所有可能的類(lèi)型。因?yàn)樵诤瘮?shù)體需要比較 T 類(lèi)型的值,不過(guò)它只能用于我們知道如何排序的類(lèi)型。為了開(kāi)啟比較功能,標(biāo)準(zhǔn)庫(kù)中定義的 std::cmp::PartialOrd trait 可以實(shí)現(xiàn)類(lèi)型的比較功能, 我們限制 T 只對(duì)實(shí)現(xiàn)了 PartialOrd 的類(lèi)型有效后代碼就可以編譯了,因?yàn)闃?biāo)準(zhǔn)庫(kù)為 i32 和 char 實(shí)現(xiàn)了 PartialOrd。

3.在結(jié)構(gòu)體中使用泛型

同樣也可以用 <> 語(yǔ)法來(lái)定義結(jié)構(gòu)體,它包含一個(gè)或多個(gè)泛型參數(shù)類(lèi)型字段。下面的代碼片段定義了一個(gè)可以存放任何類(lèi)型的 x 和 y 坐標(biāo)值的結(jié)構(gòu)體 Point:

struct Point<T> {
    x: T,
    y: T,
}

fn main() {
    let integer = Point { x: 5, y: 10 };
    let float = Point { x: 1.0, y: 4.0 };
}

其語(yǔ)法類(lèi)似于函數(shù)定義中使用泛型。首先,必須在結(jié)構(gòu)體名稱(chēng)后面的尖括號(hào)中聲明泛型參數(shù)的名稱(chēng)。接著在結(jié)構(gòu)體定義中可以指定具體數(shù)據(jù)類(lèi)型的位置使用泛型類(lèi)型。

注意 Point<T> 的定義中只使用了一個(gè)泛型類(lèi)型,這個(gè)定義表明結(jié)構(gòu)體 Point<T> 對(duì)于一些類(lèi)型 T 是泛型的,而且字段 x 和 y 都是 相同類(lèi)型的,無(wú)論它具體是何類(lèi)型。

如果嘗試創(chuàng)建一個(gè)有不同類(lèi)型值的 Point<T> 的實(shí)例, 看下面的代碼:

struct Point<T> {
    x: T,
    y: T,
}

fn main() {
    let wont_work = Point { x: 5, y: 4.0 };
}

在這個(gè)例子中,當(dāng)把整型值 5 賦值給 x 時(shí),就告訴了編譯器這個(gè) Point<T> 實(shí)例中的泛型 T 全是整型。接著指定 y 為浮點(diǎn)值 4.0,因?yàn)樗鼀被定義為與 x 相同類(lèi)型,所以將會(huì)得到一個(gè)像這樣的類(lèi)型不匹配錯(cuò)誤:

如果想要定義一個(gè) x 和 y 可以有不同類(lèi)型且仍然是泛型的 Point 結(jié)構(gòu)體,我們可以使用多個(gè)泛型類(lèi)型參數(shù)。修改 Point 的定義為擁有兩個(gè)泛型類(lèi)型 T 和 U。其中字段 x 是 T 類(lèi)型的,而字段 y 是 U 類(lèi)型的:

struct Point<T, U> {
    x: T,
    y: U,
}

fn main() {
    let both_integer = Point { x: 5, y: 10 };
    let both_float = Point { x: 1.0, y: 4.0 };
    let integer_and_float = Point { x: 5, y: 4.0 };
}

現(xiàn)在所有這些 Point 實(shí)例都合法了!我們可以在定義中使用任意多的泛型類(lèi)型參數(shù),不過(guò)太多的話,代碼將難以閱讀和理解。當(dāng)你發(fā)現(xiàn)代碼中需要很多泛型時(shí),這可能表明你的代碼需要重構(gòu)分解成更小的結(jié)構(gòu)。

4.枚舉中使用泛型

和結(jié)構(gòu)體類(lèi)似,枚舉也可以在成員中存放泛型數(shù)據(jù)類(lèi)型。例如:

enum Option<T> {
    Some(T),
    None,
}

Option<T> 是一個(gè)擁有泛型 T 的枚舉,它有兩個(gè)成員:Some,它存放了一個(gè)類(lèi)型 T 的值,和不存在任何值的None。通過(guò) Option<T> 枚舉可以表達(dá)有一個(gè)可能的值的抽象概念,同時(shí)因?yàn)?nbsp;Option<T> 是泛型的,無(wú)論這個(gè)可能的值是什么類(lèi)型都可以使用這個(gè)抽象。

枚舉也可以擁有多個(gè)泛型類(lèi)型, 例如:

enum Result<T, E> {
    Ok(T),
    Err(E),
}

Result 枚舉有兩個(gè)泛型類(lèi)型,T 和 E。Result 有兩個(gè)成員:Ok,它存放一個(gè)類(lèi)型 T 的值,而 Err 則存放一個(gè)類(lèi)型 E 的值。這個(gè)定義使得 Result 枚舉能很方便的表達(dá)任何可能成功(返回 T 類(lèi)型的值)也可能失?。ǚ祷?nbsp;E 類(lèi)型的值)的操作。

總結(jié):當(dāng)意識(shí)到代碼中定義了多個(gè)結(jié)構(gòu)體或枚舉,它們不一樣的地方只是其中的值的類(lèi)型的時(shí)候,不妨通過(guò)泛型類(lèi)型來(lái)避免重復(fù)。

5.方法定義中的泛型

在為結(jié)構(gòu)體和枚舉實(shí)現(xiàn)方法時(shí), 一樣也可以用泛型。看下面的代碼:

struct Point<T> {
    x: T,
    y: T,
}

impl<T> Point<T> {
    fn x(&self) -> &T {
        &self.x
    }
}

fn main() {
    let p = Point { x: 5, y: 10 };

    println!("p.x = {}", p.x());
}

這里在 Point<T> 上定義了一個(gè)叫做 x 的方法來(lái)返回字段 x 中數(shù)據(jù)的引用。注意必須在 impl 后面聲明 T,這樣就可以在 Point<T> 上實(shí)現(xiàn)的方法中使用 T 了。通過(guò)在 impl 之后聲明泛型 T,Rust 就知道 Point 的尖括號(hào)中的類(lèi)型是泛型而不是具體類(lèi)型。我們可以為泛型參數(shù)選擇一個(gè)與結(jié)構(gòu)體定義中聲明的泛型參數(shù)所不同的名稱(chēng),不過(guò)依照慣例使用了相同的名稱(chēng)。impl 中編寫(xiě)的方法聲明了泛型類(lèi)型可以定位為任何類(lèi)型的實(shí)例,不管最終替換泛型類(lèi)型的是何具體類(lèi)型。

定義方法時(shí)也可以為泛型指定限制(constraint)。例如,可以選擇為 Point<f32> 實(shí)例實(shí)現(xiàn)方法,而不是為泛型 Point 實(shí)例。代碼如下:

impl Point<f32> {
    fn distance_from_origin(&self) -> f32 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}

這段代碼意味著 Point<f32> 類(lèi)型會(huì)有一個(gè)方法 distance_from_origin,而其他 T 不是 f32 類(lèi)型的 Point<T> 實(shí)例則沒(méi)有定義此方法。這個(gè)方法計(jì)算點(diǎn)實(shí)例與坐標(biāo) (0.0, 0.0) 之間的距離,并使用了只能用于浮點(diǎn)型的數(shù)學(xué)運(yùn)算符。

結(jié)構(gòu)體定義中的泛型類(lèi)型參數(shù)并不總是與結(jié)構(gòu)體方法簽名中使用的泛型是同一類(lèi)型??聪旅娴拇a:

struct Point<X1, Y1> {
    x: X1,
    y: Y1,
}

impl<X1, Y1> Point<X1, Y1> {
    fn mixup<X2, Y2>(self, other: Point<X2, Y2>) -> Point<X1, Y2> {
        Point {
            x: self.x,
            y: other.y,
        }
    }
}

fn main() {
    let p1 = Point { x: 5, y: 10.4 };
    let p2 = Point { x: "Hello", y: 'c' };

    let p3 = p1.mixup(p2);

    println!("p3.x = {}, p3.y = {}", p3.x, p3.y);
}

在上面的代碼中, Point 結(jié)構(gòu)體使用了泛型類(lèi)型 X1 和 Y1,為 mixup 方法簽名使用了 X2 和 Y2 來(lái)使得示例更加清楚。這個(gè)方法用 self 的 Point 類(lèi)型的 x 值(類(lèi)型 X1)和參數(shù)的 Point 類(lèi)型的 y 值(類(lèi)型 Y2)來(lái)創(chuàng)建一個(gè)新 Point 類(lèi)型的實(shí)例

在 main 函數(shù)中,定義了一個(gè)有 i32 類(lèi)型的 x(其值為 5)和 f64 的 y(其值為 10.4)的 Point。p2 則是一個(gè)有著字符串 slice 類(lèi)型的 x(其值為 "Hello")和 char 類(lèi)型的 y(其值為c)的 Point。在 p1 上以 p2 作為參數(shù)調(diào)用 mixup 會(huì)返回一個(gè) p3,它會(huì)有一個(gè) i32 類(lèi)型的 x,因?yàn)?nbsp;x 來(lái)自 p1,并擁有一個(gè) char 類(lèi)型的 y,因?yàn)?nbsp;y 來(lái)自 p2。println! 會(huì)打印出 p3.x = 5, p3.y = c。

這個(gè)例子的目的是展示一些泛型通過(guò) impl 聲明而另一些通過(guò)方法定義聲明的情況。這里泛型參數(shù) X1 和 Y1 聲明于 impl 之后,因?yàn)樗鼈兣c結(jié)構(gòu)體定義相對(duì)應(yīng)。而泛型參數(shù) X2 和 Y2 聲明于 fn mixup 之后,因?yàn)樗鼈冎皇窍鄬?duì)于方法本身的。

6.泛型代碼性能

不用擔(dān)心使用泛型會(huì)比使用具體類(lèi)型的代碼性能低。

Rust 通過(guò)在編譯時(shí)進(jìn)行泛型代碼的 單態(tài)化(monomorphization)來(lái)保證效率。單態(tài)化是一個(gè)通過(guò)填充編譯時(shí)使用的具體類(lèi)型,將通用代碼轉(zhuǎn)換為特定代碼的過(guò)程。

在這個(gè)過(guò)程中,編譯器尋找所有泛型代碼被調(diào)用的位置并使用泛型代碼針對(duì)具體類(lèi)型生成代碼。

下面看看這個(gè)怎樣用于標(biāo)準(zhǔn)庫(kù)中的 Option 枚舉:

let integer = Some(5);
let float = Some(5.0);

當(dāng) Rust 編譯這些代碼的時(shí)候,它會(huì)進(jìn)行單態(tài)化。編譯器會(huì)讀取傳遞給 Option<T> 的值并發(fā)現(xiàn)有兩種 Option<T>:一個(gè)對(duì)應(yīng) i32 另一個(gè)對(duì)應(yīng) f64。為此,它會(huì)將泛型定義 Option<T> 展開(kāi)為兩個(gè)針對(duì) i32 和 f64 的定義,接著將泛型定義替換為這兩個(gè)具體的定義。

編譯器生成的單態(tài)化版本的代碼看起來(lái)像這樣(編譯器會(huì)使用不同于如下假想的名字):

enum Option_i32 {
    Some(i32),
    None,
}

enum Option_f64 {
    Some(f64),
    None,
}

fn main() {
    let integer = Option_i32::Some(5);
    let float = Option_f64::Some(5.0);
}

泛型 Option<T> 被編譯器替換為了具體的定義。因?yàn)?Rust 會(huì)將每種情況下的泛型代碼編譯為具體類(lèi)型,使用泛型沒(méi)有運(yùn)行時(shí)開(kāi)銷(xiāo)。當(dāng)代碼運(yùn)行時(shí),它的執(zhí)行效率就跟好像手寫(xiě)每個(gè)具體定義的重復(fù)代碼一樣。這個(gè)單態(tài)化過(guò)程正是 Rust 泛型在運(yùn)行時(shí)極其高效的原因。

責(zé)任編輯:趙寧寧 來(lái)源: 二進(jìn)制空間安全
相關(guān)推薦

2024-01-07 17:29:10

編程語(yǔ)言線程Rust

2023-12-28 10:30:56

類(lèi)型系統(tǒng)Rust

2023-01-05 17:13:28

TypeScript泛型組件

2023-06-15 17:00:11

Rust循環(huán)

2024-03-26 09:25:35

RustSerde重命名

2023-05-04 07:33:39

Rust變量常量

2021-03-22 08:45:30

異步編程Java

2020-11-13 07:19:45

元編程模型Java

2024-09-06 11:34:15

RustAI語(yǔ)言

2021-09-29 18:17:30

Go泛型語(yǔ)言

2024-02-07 11:44:20

NestJSRxJS異步編程

2011-12-08 10:24:53

JavaNIO

2024-04-07 00:00:10

Rust枚舉C代碼

2024-10-28 00:40:49

Go語(yǔ)法版本

2011-06-08 16:56:37

VS2008 Qt

2009-08-24 18:22:05

C# 泛型編程

2022-08-09 07:08:28

函數(shù)式編程Go

2009-05-27 10:12:27

LINQ泛型字典Dictionary

2009-04-24 09:33:12

LINQ查詢(xún)非泛型

2019-01-17 10:25:56

Python編程語(yǔ)言程序員
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)