Rust指南之泛型與特性詳解
前言
在上篇Rust 文章中涉及到了泛型的知識(shí),那么今天就來(lái)詳細(xì)介紹一下Rust 中的泛型與特性。泛型是一個(gè)編程語(yǔ)言不可或缺的機(jī)制,例如在C++ 語(yǔ)言中用模板來(lái)實(shí)現(xiàn)泛型。泛型機(jī)制是編程語(yǔ)言用于表達(dá)類(lèi)型抽象的機(jī)制,一般用于功能確定、數(shù)據(jù)類(lèi)型待定的類(lèi),如鏈表、映射表等。
1、泛型
泛型是具體類(lèi)型或其他屬性的抽象代替:
- 所編寫(xiě)的泛型代碼并非最終程序運(yùn)行的代碼,而是一種模板,含有一些"占位符"
- 編譯器在編譯的時(shí)候?qū)?quot;占位符" 替換為具體的數(shù)據(jù)類(lèi)型
優(yōu)點(diǎn):
提高代碼復(fù)用能力
- 減少代碼重復(fù)
1.1、在函數(shù)中定義泛型
例如,定義一個(gè)對(duì)整型數(shù)字選擇排序的函數(shù):
fn max(array: &[i32]) -> i32 {
let mut max_index = 0;
let mut i = 1;
while i < array.len() {
if array[i] > array[max_index] {
max_index = i;
}
i += 1;
}
array[max_index]
}
fn main() {
let a = [3, 4, 6, 8, 1];
println!("max = {}", max(&a));
}
//運(yùn)行結(jié)果:max = 8
這是一個(gè)簡(jiǎn)單的取最大值程序,可以用于處理
i32數(shù)字類(lèi)型的數(shù)據(jù),但無(wú)法用于f64類(lèi)型的數(shù)據(jù)。
通過(guò)使用泛型我們可以使這個(gè)函數(shù)可以利用到各個(gè)類(lèi)型中去:
fn max<T>(array: &[T]) -> T {
let mut max_index = 0;
let mut i = 1;
while i < array.len() {
if array[i] > array[max_index] {
max_index = i;
}
i += 1;
}
array[max_index]
}
實(shí)際上,并不是所有的數(shù)據(jù)類(lèi)型都可以比大小。當(dāng)T被自定義的結(jié)構(gòu)體或者枚舉等類(lèi)型替代時(shí),這段代碼肯定就會(huì)報(bào)錯(cuò)。所以這段代碼并不是用來(lái)運(yùn)行的,而是用來(lái)描述一下函數(shù)泛型的語(yǔ)法格式。
1.2、結(jié)構(gòu)體中的泛型
結(jié)構(gòu)體泛型舉例:點(diǎn)坐標(biāo)結(jié)構(gòu)體,T 表示描述點(diǎn)坐標(biāo)的數(shù)據(jù)類(lèi)型:
struct Point<T> {
x: T,
y: T
}
fn main() {
let p1 = Point {x: 1, y: 2};
let p2 = Point {x: 1.0, y: 2.0};
}
使用時(shí)并沒(méi)有聲明類(lèi)型,這里使用的是自動(dòng)類(lèi)型機(jī)制,但不允許出現(xiàn)類(lèi)型不匹配的情況如下:
let p = Point {x: 1, y: 2.0};
x 與 1 綁定時(shí)就已經(jīng)將 T 設(shè)定為 i32,所以不允許再出現(xiàn) f64 的類(lèi)型。如果我們想讓 x 與 y 用不同的數(shù)據(jù)類(lèi)型表示,可以使用兩個(gè)泛型標(biāo)識(shí)符:
struct Point<T1, T2> {
x: T1,
y: T2
}
1.3、枚舉類(lèi)中的泛型
在枚舉類(lèi)中表示泛型的方法諸如 Option 和 Result:
enum Option<T> {
Some(T),
None,
}
enum Result<T, E> {
Ok(T),
Err(E),
}
枚舉類(lèi)的具體使用可參考本專(zhuān)欄的文章,有較為詳細(xì)的講解。
1.4、方法中的泛型
結(jié)構(gòu)體與枚舉類(lèi)都可以定義方法,那么方法也應(yīng)該實(shí)現(xiàn)泛型的機(jī)制,否則泛型的類(lèi)將無(wú)法被有效的方法操作。
struct Point<T> {
x: T,
y: T
}
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
fn main() {
let p = Point { x: 2, y: 4 };
println!("p.x = {}", p.x());
}
//運(yùn)行結(jié)果:p.x = 1
注意,
impl關(guān)鍵字的后方必須有<T>,因?yàn)樗竺娴?T 是以之為榜樣的。
我們也可以為其中的一種泛型添加方法:
impl Point<i64> {
fn x(&self) -> i64 {
self.x
}
}
impl 塊本身的泛型并沒(méi)有阻礙其內(nèi)部方法具有泛型的能力
例如:
impl<T, U> Point<T, U> {
fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
Point {
x: self.x,
y: other.y,
}
}
}
方法
mixup將一個(gè) Point<T, U> 點(diǎn)的x與 Point<V, W> 點(diǎn)的y融合成一個(gè)類(lèi)型為Point<T, W>的新點(diǎn)。
2、特性
特性(trait)概念接近于 Java 中的接口(Interface),但兩者不完全相同。特性與接口相同的地方在于它們都是一種行為規(guī)范,可以用于標(biāo)識(shí)哪些類(lèi)有哪些方法。
特性在 Rust 中用 trait 表示:
trait Descript {
fn describe(&self) -> String;
}
Descript 規(guī)定了實(shí)現(xiàn)者必需有 describe(&self) -> String 方法。
例如:
struct Person {
name: String,
age: u16
}
impl Descript for Person {
fn describe(&self) -> String {
format!("{} {}", self.name, self.age)
}
}
格式:
- impl <特性名> for <所實(shí)現(xiàn)的類(lèi)型名>
Rust 同一個(gè)類(lèi)可以實(shí)現(xiàn)多個(gè)特性,每個(gè) impl 塊只能實(shí)現(xiàn)一個(gè)
2.1、默認(rèn)特性
這是特性與接口的不同點(diǎn):
- 接口只能規(guī)范方法而不能定義方法
- 特性可以定義方法作為默認(rèn)方法
- 因?yàn)槭?quot;默認(rèn)",所以對(duì)象對(duì)于是否重新定義方法是自由的
舉個(gè)例子:
trait Descript {
fn describe(&self) -> String {
String::from("[Object]")
}
}
struct Person {
name: String,
age: u8
}
impl Descript for Person {
fn describe(&self) -> String {
format!("{} {}", self.name, self.age)
}
}
fn main() {
let zhangsan = Person {
name: String::from("kuangtu"),
age: 28
};
println!("{}", zhangsan.describe());
}
//運(yùn)行結(jié)果:kuangtu 28
如果將
impl Descript for Person塊中的內(nèi)容去掉,那么運(yùn)行結(jié)果就是 [Object]
2.2、特性做參數(shù)
很多情況下我們需要傳遞一個(gè)函數(shù)做參數(shù),例如回調(diào)函數(shù)、設(shè)置按鈕事件等。在 Java 中函數(shù)必須以接口實(shí)現(xiàn)的類(lèi)實(shí)例來(lái)傳遞,在 Rust 中可以通過(guò)傳遞特性參數(shù)來(lái)實(shí)現(xiàn):
fn output(object: impl Descript) {
println!("{}", object.describe());
}
任何實(shí)現(xiàn)了
Descript特性的對(duì)象都可以作為這個(gè)函數(shù)的參數(shù),這個(gè)函數(shù)沒(méi)必要知道傳入對(duì)象有沒(méi)有其他屬性或方法,只需要了解它一定有 Descript 特性規(guī)范的方法就可以了。當(dāng)然,此函數(shù)內(nèi)也無(wú)法使用其他的屬性與方法。
特性參數(shù)還可以用這種等效語(yǔ)法實(shí)現(xiàn):
fn output<T: Descriptive>(object: T) {
println!("{}", object.describe());
}
這是一種風(fēng)格類(lèi)似泛型的語(yǔ)法糖,這種語(yǔ)法糖在有多個(gè)參數(shù)類(lèi)型均是特性的情況下十分實(shí)用:
fn output_two<T: Descriptive>(arg1: T, arg2: T) {
println!("{}", arg1.describe());
println!("{}", arg2.describe());
}
特性作類(lèi)型表示時(shí)如果涉及多個(gè)特性,可以用 + 符號(hào)表示,例如:
fn notify(item: impl Summary + Display) fn notify<T: Summary + Display>(item: T)
注意:僅用于表示類(lèi)型的時(shí)候,并不可以在
impl塊中使用。
復(fù)雜的實(shí)現(xiàn)關(guān)系可以使用 where 關(guān)鍵字簡(jiǎn)化,例如:
fn some_function<T: Display + Clone, U: Clone + Debug>(t: T, u: U)
可以簡(jiǎn)化為:
fn some_function<T, U>(t: T, u: U) -> i32
where T: Display + Clone,
U: Clone + Debug
泛型通過(guò)與特性的結(jié)合可以實(shí)現(xiàn)上面任意類(lèi)型值比較的案例:
trait Comparable {
fn compare(&self, object: &Self) -> i8;
}
fn max<T: Comparable>(array: &[T]) -> &T {
let mut max_index = 0;
let mut i = 1;
while i < array.len() {
if array[i].compare(&array[max_index]) > 0 {
max_index = i;
}
i += 1;
}
&array[max_index]
}
impl Comparable for f64 {
fn compare(&self, object: &f64) -> i8 {
if &self > &object { 1 }
else if &self == &object { 0 }
else { -1 }
}
}
fn main() {
let arr = [1.0, 3.0, 7.0, 4.0, 2.0];
println!("maximum of arr is {}", max(&arr));
}
//運(yùn)行結(jié)果:maximum of arr is 7
Tip: 由于需要聲明
compare函數(shù)的第二參數(shù)必須與實(shí)現(xiàn)該特性的類(lèi)型相同,所以Self(注意大小寫(xiě))關(guān)鍵字就代表了當(dāng)前類(lèi)型(不是實(shí)例)本身。
2.3、特性做返回值
格式如下:
fn person() -> impl Descript {
Person {
name: String::from("Cali"),
age: 24
}
}
注意:特性做返回值只接受實(shí)現(xiàn)了該特性的對(duì)象做返回值且在同一個(gè)函數(shù)中所有可能的返回值類(lèi)型必須完全一樣。
比如結(jié)構(gòu)體 A 與結(jié)構(gòu)體 B 都實(shí)現(xiàn)了特性 Trait,下面這個(gè)函數(shù)就是錯(cuò)誤的:
fn some_function(bool bl) -> impl Descriptive {
if bl {
return A {};
} else {
return B {};
}
}
到此這篇關(guān)于Rust指南泛型與特性的文章就介紹到這了,更多相關(guān)Rust泛型與特性?xún)?nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Rust中類(lèi)型轉(zhuǎn)換在錯(cuò)誤處理中的應(yīng)用小結(jié)
隨著項(xiàng)目的進(jìn)展,關(guān)于Rust的故事又翻開(kāi)了新的一頁(yè),今天來(lái)到了服務(wù)器端的開(kāi)發(fā)場(chǎng)景,發(fā)現(xiàn)錯(cuò)誤處理中的錯(cuò)誤類(lèi)型轉(zhuǎn)換有必要分享一下,對(duì)Rust錯(cuò)誤處理相關(guān)知識(shí)感興趣的朋友一起看看吧2023-09-09
Rust如何使用Sauron實(shí)現(xiàn)Web界面交互
Sauron?是一個(gè)多功能的?Web?框架和庫(kù),用于構(gòu)建客戶(hù)端和/或服務(wù)器端?Web?應(yīng)用程序,重點(diǎn)關(guān)注人體工程學(xué)、簡(jiǎn)單性和優(yōu)雅性,這篇文章主要介紹了Rust使用Sauron實(shí)現(xiàn)Web界面交互,需要的朋友可以參考下2024-03-03
在win10上使用mingw64編譯器配置Rust開(kāi)發(fā)環(huán)境和idea 配置Rust 插件
在win10上配置 Rust 開(kāi)發(fā)環(huán)境(使用 mingw64編譯器)和 idea 配置 Rust 插件的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-03-03
Rust中GUI庫(kù)egui的簡(jiǎn)單應(yīng)用指南
egui(發(fā)音為“e-gooey”)是一個(gè)簡(jiǎn)單、快速且高度可移植的 Rust 即時(shí)模式 GUI 庫(kù),跨平臺(tái)、Rust原生,適合一些小工具和游戲引擎GUI,下面就跟隨小編一起來(lái)看看它的具體使用吧2024-03-03
Rust中的Iterator和IntoIterator介紹及應(yīng)用小結(jié)
Iterator即迭代器,它可以用于對(duì)數(shù)據(jù)結(jié)構(gòu)進(jìn)行迭代,被迭代的數(shù)據(jù)結(jié)構(gòu)是可迭代的(iterable),所謂的可迭代就是這個(gè)數(shù)據(jù)結(jié)構(gòu)有返回迭代器的方法,這篇文章主要介紹了Rust中的Iterator和IntoIterator介紹及應(yīng)用,需要的朋友可以參考下2023-07-07

