網站首頁 編程語言 正文
前言
在上篇Rust 文章中涉及到了泛型的知識,那么今天就來詳細介紹一下Rust 中的泛型與特性。泛型是一個編程語言不可或缺的機制,例如在C++ 語言中用模板來實現泛型。泛型機制是編程語言用于表達類型抽象的機制,一般用于功能確定、數據類型待定的類,如鏈表、映射表等。
1、泛型
泛型是具體類型或其他屬性的抽象代替:
- 所編寫的泛型代碼并非最終程序運行的代碼,而是一種模板,含有一些"占位符"
- 編譯器在編譯的時候將"占位符" 替換為具體的數據類型
優點:
提高代碼復用能力
- 減少代碼重復
1.1、在函數中定義泛型
例如,定義一個對整型數字選擇排序的函數:
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
被自定義的結構體或者枚舉等類型替代時,這段代碼肯定就會報錯。所以這段代碼并不是用來運行的,而是用來描述一下函數泛型的語法格式。
1.2、結構體中的泛型
結構體泛型舉例:點坐標結構體,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 }
1.3、枚舉類中的泛型
在枚舉類中表示泛型的方法諸如 Option
和 Result
:
enum Option<T> { Some(T), None, } enum Result<T, E> { Ok(T), Err(E), }
枚舉類的具體使用可參考本專欄的文章,有較為詳細的講解。
1.4、方法中的泛型
結構體與枚舉類都可以定義方法,那么方法也應該實現泛型的機制,否則泛型的類將無法被有效的方法操作。
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>
的新點。
2、特性
特性(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) } }
格式:
- impl <特性名> for <所實現的類型名>
Rust 同一個類可以實現多個特性,每個 impl 塊只能實現一個
2.1、默認特性
這是特性與接口的不同點:
- 接口只能規范方法而不能定義方法
- 特性可以定義方法作為默認方法
- 因為是"默認",所以對象對于是否重新定義方法是自由的
舉個例子:
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]
2.2、特性做參數
很多情況下我們需要傳遞一個函數做參數,例如回調函數、設置按鈕事件等。在 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
(注意大小寫)關鍵字就代表了當前類型(不是實例)本身。
2.3、特性做返回值
格式如下:
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 {}; } }
原文鏈接:https://blog.csdn.net/m0_58618795/article/details/127190345
相關推薦
- 2022-07-27 Python?迭代器Iterator詳情_python
- 2022-04-12 error: failed to push some refs to 'git@gitlab.xxx
- 2022-07-08 C#中的Dialog對話框_C#教程
- 2022-06-17 C語言深入講解棧與堆和靜態存儲區的使用_C 語言
- 2022-11-13 Python實現簡單的文件操作合集_python
- 2022-07-25 Android開發之Fragment懶加載的幾種方式及性能對比_Android
- 2022-07-31 ubuntu下常用apt命令介紹_linux shell
- 2022-07-14 Android實現手機多點觸摸畫圓_Android
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支