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

成都創(chuàng)新互聯(lián)公司長期為成百上千客戶提供的網(wǎng)站建設(shè)服務(wù),團隊從業(yè)經(jīng)驗10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為沐川企業(yè)提供專業(yè)的成都網(wǎng)站制作、網(wǎng)站建設(shè),沐川網(wǎng)站改版等技術(shù)服務(wù)。擁有10余年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。
2.在函數(shù)定義中使用泛型
當(dāng)使用泛型定義函數(shù)時,本來在函數(shù)簽名中指定參數(shù)和返回值的類型的地方,會改用泛型來表示。采用這種技術(shù),使得代碼適應(yīng)性更強,從而為函數(shù)的調(diào)用者提供更多的功能,同時也避免了代碼的重復(fù)。
看下面的代碼例子, 定義了兩個函數(shù), 功能都差不多,作用是分別尋找slice中最大的i32和slice中最大的char, 只是數(shù)據(jù)類型不同。
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)在需要定義一個新函數(shù), 引進泛型參數(shù)來消除這種因數(shù)據(jù)類型不同而導(dǎo)致的函數(shù)重復(fù)定義。為了參數(shù)化這個新函數(shù)中的這些類型,我們需要為類型參數(shù)命名,道理和給函數(shù)的形參起名一樣。任何標(biāo)識符都可以作為類型參數(shù)的名字。這里選用 T,因為傳統(tǒng)上來說,Rust 的類型參數(shù)名字都比較短,通常僅為一個字母,同時,Rust 類型名的命名規(guī)范是首字母大寫駝峰式命名法(UpperCamelCase)。T 作為 “type” 的縮寫是大部分 Rust 程序員的首選。
如果要在函數(shù)體中使用參數(shù),就必須在函數(shù)簽名中聲明它的名字,好讓編譯器知道這個名字指代的是什么。同理,當(dāng)在函數(shù)簽名中使用一個類型參數(shù)時,必須在使用它之前就聲明它。為了定義泛型版本的 largest 函數(shù),類型參數(shù)聲明位于函數(shù)名稱與參數(shù)列表中間的尖括號 <> 中,像這樣:
fn largest(list: &[T]) -> &T 可以這樣理解這個定義:函數(shù) largest 有泛型類型 T。它有個參數(shù) list,其類型是元素為 T 的 slice。largest 函數(shù)會返回一個與 T 相同類型的引用。
按照這個思想, 我們將代碼改造如下:
fn largest(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é)果如下:
這次編譯沒有通過的原因Rust編譯器用綠色標(biāo)識出來了, 缺少一個: std:cmp::PartialOrd, 先暫且認(rèn)為這個是Rust標(biāo)準(zhǔn)庫要求的東西, 加上重新編譯一下試試:
fn largest(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
} 重新編譯結(jié)果如下:
我們在代碼中下了一個斷點, 能夠執(zhí)行到此處說明代碼已經(jīng)沒有問題。實際上上面這個錯誤表明 largest 的函數(shù)體不能適用于 T 的所有可能的類型。因為在函數(shù)體需要比較 T 類型的值,不過它只能用于我們知道如何排序的類型。為了開啟比較功能,標(biāo)準(zhǔn)庫中定義的 std::cmp::PartialOrd trait 可以實現(xiàn)類型的比較功能, 我們限制 T 只對實現(xiàn)了 PartialOrd 的類型有效后代碼就可以編譯了,因為標(biāo)準(zhǔn)庫為 i32 和 char 實現(xiàn)了 PartialOrd。
3.在結(jié)構(gòu)體中使用泛型
同樣也可以用 <> 語法來定義結(jié)構(gòu)體,它包含一個或多個泛型參數(shù)類型字段。下面的代碼片段定義了一個可以存放任何類型的 x 和 y 坐標(biāo)值的結(jié)構(gòu)體 Point:
struct Point {
x: T,
y: T,
}
fn main() {
let integer = Point { x: 5, y: 10 };
let float = Point { x: 1.0, y: 4.0 };
} 其語法類似于函數(shù)定義中使用泛型。首先,必須在結(jié)構(gòu)體名稱后面的尖括號中聲明泛型參數(shù)的名稱。接著在結(jié)構(gòu)體定義中可以指定具體數(shù)據(jù)類型的位置使用泛型類型。
注意 Point
如果嘗試創(chuàng)建一個有不同類型值的 Point
struct Point {
x: T,
y: T,
}
fn main() {
let wont_work = Point { x: 5, y: 4.0 };
} 在這個例子中,當(dāng)把整型值 5 賦值給 x 時,就告訴了編譯器這個 Point
如果想要定義一個 x 和 y 可以有不同類型且仍然是泛型的 Point 結(jié)構(gòu)體,我們可以使用多個泛型類型參數(shù)。修改 Point 的定義為擁有兩個泛型類型 T 和 U。其中字段 x 是 T 類型的,而字段 y 是 U 類型的:
struct Point {
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ù),不過太多的話,代碼將難以閱讀和理解。當(dāng)你發(fā)現(xiàn)代碼中需要很多泛型時,這可能表明你的代碼需要重構(gòu)分解成更小的結(jié)構(gòu)。
4.枚舉中使用泛型
和結(jié)構(gòu)體類似,枚舉也可以在成員中存放泛型數(shù)據(jù)類型。例如:
enum Option {
Some(T),
None,
} Option
枚舉也可以擁有多個泛型類型, 例如:
enum Result {
Ok(T),
Err(E),
} Result 枚舉有兩個泛型類型,T 和 E。Result 有兩個成員:Ok,它存放一個類型 T 的值,而 Err 則存放一個類型 E 的值。這個定義使得 Result 枚舉能很方便的表達任何可能成功(返回 T 類型的值)也可能失敗(返回 E 類型的值)的操作。
總結(jié):當(dāng)意識到代碼中定義了多個結(jié)構(gòu)體或枚舉,它們不一樣的地方只是其中的值的類型的時候,不妨通過泛型類型來避免重復(fù)。
5.方法定義中的泛型
在為結(jié)構(gòu)體和枚舉實現(xiàn)方法時, 一樣也可以用泛型??聪旅娴拇a:
struct Point {
x: T,
y: T,
}
impl Point {
fn x(&self) -> &T {
&self.x
}
}
fn main() {
let p = Point { x: 5, y: 10 };
println!("p.x = {}", p.x());
} 這里在 Point
定義方法時也可以為泛型指定限制(constraint)。例如,可以選擇為 Point
impl Point {
fn distance_from_origin(&self) -> f32 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
} 這段代碼意味著 Point
結(jié)構(gòu)體定義中的泛型類型參數(shù)并不總是與結(jié)構(gòu)體方法簽名中使用的泛型是同一類型??聪旅娴拇a:
struct Point {
x: X1,
y: Y1,
}
impl Point {
fn mixup(self, other: Point) -> Point {
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)體使用了泛型類型 X1 和 Y1,為 mixup 方法簽名使用了 X2 和 Y2 來使得示例更加清楚。這個方法用 self 的 Point 類型的 x 值(類型 X1)和參數(shù)的 Point 類型的 y 值(類型 Y2)來創(chuàng)建一個新 Point 類型的實例
在 main 函數(shù)中,定義了一個有 i32 類型的 x(其值為 5)和 f64 的 y(其值為 10.4)的 Point。p2 則是一個有著字符串 slice 類型的 x(其值為 "Hello")和 char 類型的 y(其值為c)的 Point。在 p1 上以 p2 作為參數(shù)調(diào)用 mixup 會返回一個 p3,它會有一個 i32 類型的 x,因為 x 來自 p1,并擁有一個 char 類型的 y,因為 y 來自 p2。println! 會打印出 p3.x = 5, p3.y = c。
這個例子的目的是展示一些泛型通過 impl 聲明而另一些通過方法定義聲明的情況。這里泛型參數(shù) X1 和 Y1 聲明于 impl 之后,因為它們與結(jié)構(gòu)體定義相對應(yīng)。而泛型參數(shù) X2 和 Y2 聲明于 fn mixup 之后,因為它們只是相對于方法本身的。
6.泛型代碼性能
不用擔(dān)心使用泛型會比使用具體類型的代碼性能低。
Rust 通過在編譯時進行泛型代碼的 單態(tài)化(monomorphization)來保證效率。單態(tài)化是一個通過填充編譯時使用的具體類型,將通用代碼轉(zhuǎn)換為特定代碼的過程。
在這個過程中,編譯器尋找所有泛型代碼被調(diào)用的位置并使用泛型代碼針對具體類型生成代碼。
下面看看這個怎樣用于標(biāo)準(zhǔn)庫中的 Option 枚舉:
let integer = Some(5);
let float = Some(5.0);當(dāng) Rust 編譯這些代碼的時候,它會進行單態(tài)化。編譯器會讀取傳遞給 Option
編譯器生成的單態(tài)化版本的代碼看起來像這樣(編譯器會使用不同于如下假想的名字):
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
當(dāng)前題目:在Rust編程中使用泛型
路徑分享:http://m.fisionsoft.com.cn/article/dhpojgd.html


咨詢
建站咨詢
