<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在上篇Rust 文章中涉及到了泛型的知識,那麼今天就來詳細介紹一下Rust 中的泛型與特性。泛型是一個程式語言不可或缺的機制,例如在C++ 語言中用模板來實現泛型。泛型機制是程式語言用於表達型別抽象的機制,一般用於功能確定、資料型別待定的類,如連結串列、對映表等。
泛型是具體型別或其他屬性的抽象代替:
優點:
提高程式碼複用能力
例如,定義一個對整型數位選擇排序的函數:
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)); } //執行結果:max = 8
這是一個簡單的取最大值程式,可以用於處理
i32
數位型別的資料,但無法用於f64
型別的資料。
通過使用泛型我們可以使這個函數可以利用到各個型別中去:
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] }
實際上,並不是所有的資料型別都可以比大小。當T
被自定義的結構體或者列舉等型別替代時,這段程式碼肯定就會報錯。所以這段程式碼並不是用來執行的,而是用來描述一下函數泛型的語法格式。
結構體泛型舉例:點座標結構體,T 表示描述點座標的資料型別:
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}; }
使用時並沒有宣告型別,這裡使用的是自動型別機制,但不允許出現型別不匹配的情況如下:
let p = Point {x: 1, y: 2.0};
x 與 1 繫結時就已經將 T 設定為 i32
,所以不允許再出現 f64
的型別。如果我們想讓 x 與 y 用不同的資料型別表示,可以使用兩個泛型識別符號:
struct Point<T1, T2> { x: T1, y: T2 }
在列舉類中表示泛型的方法諸如 Option
和 Result
:
enum Option<T> { Some(T), None, } enum Result<T, E> { Ok(T), Err(E), }
列舉類的具體使用可參考本專欄的文章,有較為詳細的講解。
結構體與列舉類都可以定義方法,那麼方法也應該實現泛型的機制,否則泛型的類將無法被有效的方法操作。
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()); } //執行結果:p.x = 1
注意,
impl
關鍵字的後方必須有<T>
,因為它後面的 T 是以之為榜樣的。
我們也可以為其中的一種泛型新增方法:
impl Point<i64> { fn x(&self) -> i64 { self.x } }
impl
塊本身的泛型並沒有阻礙其內部方法具有泛型的能力
例如:
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
將一個 Point<T, U> 點的x
與 Point<V, W> 點的y
融合成一個型別為Point<T, W>
的新點。
特性(trait
)概念接近於 Java 中的介面(Interface
),但兩者不完全相同。特性與介面相同的地方在於它們都是一種行為規範,可以用於標識哪些類有哪些方法。
特性在 Rust 中用 trait 表示:
trait Descript { fn describe(&self) -> String; }
Descript
規定了實現者必需有 describe(&self) -> String
方法。
例如:
struct Person { name: String, age: u16 } impl Descript for Person { fn describe(&self) -> String { format!("{} {}", self.name, self.age) } }
格式:
Rust 同一個類可以實現多個特性,每個 impl 塊只能實現一個
這是特性與介面的不同點:
舉個例子:
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()); } //執行結果:kuangtu 28
如果將
impl Descript for Person
塊中的內容去掉,那麼執行結果就是 [Object]
很多情況下我們需要傳遞一個函數做引數,例如回撥函數、設定按鈕事件等。在 Java 中函數必須以介面實現的類範例來傳遞,在 Rust 中可以通過傳遞特性引數來實現:
fn output(object: impl Descript) { println!("{}", object.describe()); }
任何實現了
Descript
特性的物件都可以作為這個函數的引數,這個函數沒必要知道傳入物件有沒有其他屬性或方法,只需要瞭解它一定有 Descript 特性規範的方法就可以了。當然,此函數內也無法使用其他的屬性與方法。
特性引數還可以用這種等效語法實現:
fn output<T: Descriptive>(object: T) { println!("{}", object.describe()); }
這是一種風格類似泛型的語法糖,這種語法糖在有多個引數型別均是特性的情況下十分實用:
fn output_two<T: Descriptive>(arg1: T, arg2: T) { println!("{}", arg1.describe()); println!("{}", arg2.describe()); }
特性作型別表示時如果涉及多個特性,可以用 +
符號表示,例如:
fn notify(item: impl Summary + Display) fn notify<T: Summary + Display>(item: T)
注意:僅用於表示型別的時候,並不可以在
impl
塊中使用。
複雜的實現關係可以使用 where
關鍵字簡化,例如:
fn some_function<T: Display + Clone, U: Clone + Debug>(t: T, u: U)
可以簡化為:
fn some_function<T, U>(t: T, u: U) -> i32 where T: Display + Clone, U: Clone + Debug
泛型通過與特性的結合可以實現上面任意型別值比較的案例:
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)); } //執行結果:maximum of arr is 7
Tip: 由於需要宣告
compare
函數的第二引數必須與實現該特性的型別相同,所以Self
(注意大小寫)關鍵字就代表了當前型別(不是範例)本身。
格式如下:
fn person() -> impl Descript { Person { name: String::from("Cali"), age: 24 } }
注意:特性做返回值只接受實現了該特性的物件做返回值且在同一個函數中所有可能的返回值型別必須完全一樣。
比如結構體 A 與結構體 B 都實現了特性 Trait
,下面這個函數就是錯誤的:
fn some_function(bool bl) -> impl Descriptive { if bl { return A {}; } else { return B {}; } }
到此這篇關於Rust指南泛型與特性的文章就介紹到這了,更多相關Rust泛型與特性內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45