日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

corutine rust_Rust学习笔记#5:函数和trait

發(fā)布時(shí)間:2024/9/15 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 corutine rust_Rust学习笔记#5:函数和trait 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

函數(shù)

基本語法

Rust的函數(shù)使用fn關(guān)鍵字開頭,函數(shù)可以有一系列的輸入?yún)?shù),還有一個(gè)返回類型。函數(shù)返回可以使用return語句,可以使用表達(dá)式。下面是一個(gè)標(biāo)準(zhǔn)函數(shù)的示例,add函數(shù)接受兩個(gè)i32的參數(shù),然后計(jì)算它們的和并返回:

fn add(a: i32, b: i32) -> i32 {

a + b

}

println!("{}", add(1, 2)); // 輸出:3

函數(shù)返回值如果不顯示標(biāo)明,默認(rèn)是()。函數(shù)返回值類型也可以是never類型!,這一類函數(shù)叫做發(fā)散函數(shù),代表這個(gè)函數(shù)不能夠正常返回,例如:

fn diverges() -> ! {

panic!("This function never return!")

}

Rust編寫的可執(zhí)行程序的入口是fn main() -> ()函數(shù)。一般情況下,一個(gè)進(jìn)程開始執(zhí)行的時(shí)候可以接受一系列的參數(shù),退出的時(shí)候也可以返回一個(gè)錯(cuò)誤碼,所以很多編程語言會(huì)為main函數(shù)設(shè)計(jì)參數(shù)和返回值類型,例如C語言中的int main(int argc, char **argv)。但是,Rust的main函數(shù)無參數(shù)也無返回值,其傳遞參數(shù)和返回狀態(tài)碼都通過單獨(dú)的API來完成,示例如下:

fn main() {

for arg in std::env::args() {

println!("Arg: {}", arg);

}

}

可以通過std::env::args()函數(shù)獲取參數(shù),通過exit()函數(shù)的參數(shù)傳遞錯(cuò)誤碼,通過std::env::var()讀取環(huán)境變量。

函數(shù)遞歸與TCO

函數(shù)遞歸是我們常用的一種思維方式,Rust也支持函數(shù)遞歸調(diào)用。下面用經(jīng)典的Fibonacci數(shù)列來舉例:

fn fib(index: u32) -> u64 {

match index {

1 | 2 => 1,

_ => fib(index - 1) + fib(index - 2),

}

}

println!("{}", fib(25)); // 輸出:75025

談起遞歸,我們都會(huì)想到尾遞歸優(yōu)化的概念(TCO,Tail Call Optimization)。TCO可以把尾遞歸在編譯階段轉(zhuǎn)換為迭代循環(huán)從而降低時(shí)間和空間開銷,但Rust并不支持TCO,某個(gè)RFC的作者給出了以下理由:

可移植性問題,LLVM當(dāng)時(shí)在某些指定架構(gòu)上特別是MIPS和WebAssembly,不支持正確尾調(diào)用。

LLVM中正確尾調(diào)用實(shí)際上可能會(huì)由于它們當(dāng)時(shí)的實(shí)現(xiàn)方式而造成性能損失。

TCO讓調(diào)試變得更加困難,因?yàn)樗貙懥藯I系闹怠?/p>

方法

在一些編程語言中,函數(shù)和方法往往是對(duì)同一種東西的兩種稱呼,但在Rust中,它們是有明確區(qū)分的。方法和函數(shù)的語法完全相同:它們使用 fn 關(guān)鍵字和名稱聲明,可以擁有參數(shù)和返回值,同時(shí)包含在某處調(diào)用該方法時(shí)會(huì)執(zhí)行的代碼。但是,只有在結(jié)構(gòu)體上下文中被定義的函數(shù)才能稱為方法。

方法分為成員方法和靜態(tài)方法,它們的區(qū)別在于第一個(gè)參數(shù)是否為self。Rust中的Self和self都是關(guān)鍵字,其中Self是類型名,self是變量名,self代表調(diào)用該方法的結(jié)構(gòu)體實(shí)例,靜態(tài)方法屬于結(jié)構(gòu)體類型所有,所以不需要self。常見的Self和self的組合有: self: Self(獲得所有權(quán))、self: &self(僅僅讀取)、self: &mut self(做出修改),因?yàn)樗鼈兊氖褂妙l率很高,Rust也提供了相應(yīng)的語法糖來簡寫:self、&self、&mut self。

成員方法

可以使用impl為結(jié)構(gòu)體實(shí)現(xiàn)成員方法,如下面的代碼所示。將函數(shù)移到impl塊中,并將第一個(gè)參數(shù)改成self,就把函數(shù)變成了成員方法。和其他編程語言類似,調(diào)用成員方法只需在結(jié)構(gòu)體示例后加.即可。每個(gè)結(jié)構(gòu)體可以擁有多個(gè)impl塊。

struct Rectangle {

width: u32,

height: u32,

}

impl Rectangle {

fn area(&self) -> u32 {

self.width * self.height

}

}

fn main() {

let rect = Rectangle { width: 30, height: 50 };

println!(

"The area of the rectangle is {} square pixels.",

rect.area()

);

}

靜態(tài)方法

在impl塊中定義的不以self作為參數(shù)的方法就是靜態(tài)方法,見下面的代碼。可以使用結(jié)構(gòu)體名和::運(yùn)算符來調(diào)用靜態(tài)方法,例如let sq = Rectangle::square(3);來獲得一個(gè)大小為3的正方形。

// 接上面

impl Rectangle {

fn square(size: u32) -> Rectangle {

Rectangle { width: size, height: size }

}

}

trait

基本語法

trait的功能類似于Java中的接口,trait可以翻譯為“特性”,它可以為多種類型抽象出共同擁有的一些功能。一個(gè)類型的行為由其可供調(diào)用的方法構(gòu)成。如果可以對(duì)不同類型調(diào)用相同的方法的話,這些類型就可以共享相同的行為了。trait 定義將方法簽名組合起來,目的是定義一個(gè)實(shí)現(xiàn)某些目的所必需的行為的集合。這些方法既可以是成員方法,也可以是靜態(tài)方法。trait可以用關(guān)鍵字trait來聲明,例如:

trait Shape {

fn area(&self) -> u32;

fn hello();

}

上面的代碼聲明了一個(gè)名為Shape的trait,它有一個(gè)名為area的方法,即,若某個(gè)類型擁有Shape這個(gè)特性,那么它一定可以求面積。為某個(gè)類型實(shí)現(xiàn)trait使用impl...for關(guān)鍵字,例如:

struct Rectangle {

width: u32,

height: u32,

}

trait Shape {

fn area(&self) -> u32;

fn hello();

}

impl Shape for Rectangle {

fn area(&self) -> u32 {

self.width * self.height

}

fn hello() {

println!("Just say hello!")

}

}

fn main() {

let rect = Rectangle {

width: 30,

height: 50,

};

println!(

"The area of the rectangle is {} square pixels.",

rect.area()

);

Rectangle::hello();

}

trait中的方法也可以有默認(rèn)實(shí)現(xiàn),那么在針對(duì)具體類型實(shí)現(xiàn)的時(shí)候,就可以不用重寫。我們也可以利用trait為其他類型擴(kuò)展方法,哪怕這個(gè)類型不是我們自己寫的。例如,可以為內(nèi)置類型i32添加一個(gè)方法:

trait Double {

fn double(&self) -> i32;

}

impl Double for i32 {

fn double(&self) -> i32 {

*self * 2

}

}

fn main() {

let i: i32 = 5;

println!("{}", i.double()); // 輸出:10

}

孤兒規(guī)則

使用trait為類型擴(kuò)展方法也要受到一定的限制,在聲明trait和impl trait的時(shí)候,Rust規(guī)定了一個(gè)一致性規(guī)則,也稱為孤兒規(guī)則:impl塊要么與trait的聲明在同一個(gè)crate中,要么與類型的聲明在同一個(gè)crate中。也就是說,如果trait和類型都來自于外部的crate,那么便不允許為這個(gè)類型實(shí)現(xiàn)該trait。這是因?yàn)?#xff0c;該類型沒有實(shí)現(xiàn)該trait,這可能是該類型作者有意的設(shè)計(jì),強(qiáng)行實(shí)現(xiàn)可能會(huì)導(dǎo)致bug。

trait和接口的區(qū)別

之前我們說trait和接口在功能上類似,但它們?cè)谑褂弥惺怯袇^(qū)別的。trait本身不是具體類型,也不是指針類型,它只是定義了針對(duì)類型的抽象的約束,不同的類型可以實(shí)現(xiàn)同一個(gè)trait,而這些類型可能具有不同的大小,因此trait在編譯階段沒有固定大小,所以,Rust中不能直接使用trait作為實(shí)例變量、參數(shù)和返回值,這一點(diǎn)和接口的習(xí)慣用法是不同的。例如,下面的代碼就是編譯錯(cuò)誤的:

trait Shape {

fn area(&self) -> u32;

}

// error[E0277]: the size for values of type `(dyn Shape + 'static)` cannot be known at compilation time

fn use_shape(arg: Shape) {}

trait繼承

trait允許繼承,例如:

trait Base{}

trait Derived: Base {}

這表示Derived繼承了Base,它意味著,滿足Derived的類型,必然也滿足Base,所以,在針對(duì)一個(gè)具體類型impl Derived的時(shí)候,編譯器也會(huì)要求同時(shí)impl Base,否則會(huì)報(bào)編譯錯(cuò)誤:

trait Base {}

trait Derived: Base {}

struct T;

// error[E0277]: the trait bound `T: Base` is not satisfied

impl Derived for T {}

fn main() {}

常見trait簡介

標(biāo)準(zhǔn)庫中有很多常見且很有用的trait,我們一起學(xué)習(xí)一下。

Display和Debug

Display和Debug的定義如下:

pub trait Display {

fn fmt(&self, f: &mut Formatter) -> Result;

}

pub trait Debug {

fn fmt(&self, f: &mut Formatter) -> Result;

}

這兩個(gè)trait主要用在類似println!這樣進(jìn)行輸出的地方。只有實(shí)現(xiàn)了Display的類型,才能用{}格式打印出來;只有實(shí)現(xiàn)了Debug的類型,才能用{:?}和{:#?}格式打印出來。它們之間的更多區(qū)別如下:

Display假定了這個(gè)類型可以用utf-8格式的字符串表示,它是準(zhǔn)備給最終用戶看的,并不是所有的類型都應(yīng)該實(shí)現(xiàn)這個(gè)trait。標(biāo)準(zhǔn)庫中還有一個(gè)常用的trait叫作std::string::ToString,對(duì)于所有實(shí)現(xiàn)Display的類型, 都自動(dòng)實(shí)現(xiàn)了這個(gè)ToStringtrait,它包含了一個(gè)to_string(&self)->String方法。

Debug主要是為了調(diào)試使用,建議所有的作為API的公開類型都應(yīng)當(dāng)實(shí)現(xiàn)這個(gè)trait,以方便調(diào)試。

PartialOrd/Ord/PartialEq/Eq

我們首先介紹一下全序和偏序的概念。對(duì)于集合X中的元素a,b,c:

如果a < b則一定有!(a > b),稱為反對(duì)稱性;

如果a < b且b < c則一定有a < c,稱為傳遞性;

對(duì)于X中的所有元素,都存在a < b或a > b或者a == b,三者必居其一,稱為完全性。

如果集合中的元素只具備上述前兩條特征,則稱X是偏序;同時(shí)具備以上所有特征,則稱X是全序。

Rust設(shè)計(jì)了兩個(gè)trait來對(duì)全序和偏序進(jìn)行描述,PartialOrd代表偏序,Ord代表全序。只有滿足全序的類型才可以進(jìn)行排序,像浮點(diǎn)數(shù)這樣的偏序類型就無法排序。同理,PartialEq用來描述只能部分元素進(jìn)行相等比較,Eq表示全部元素都可以進(jìn)行相等比較。這樣的設(shè)計(jì)可以讓我們?cè)诟绲碾A段發(fā)現(xiàn)錯(cuò)誤。

Sized

這個(gè)trait表示類型是否有大小,它定義在std::marker模塊中,它沒有任何的成員方法,它與普通trait不同,編譯器對(duì)它有特殊處理,用戶也不能針對(duì)自己的類型實(shí)現(xiàn)這個(gè)trait。一個(gè)類型是否滿足Sized約束完全是由編譯器推導(dǎo)的,用戶無權(quán)指定。

Default

Rust中沒有C++中構(gòu)造函數(shù)的概念,因?yàn)橄啾绕胀ê瘮?shù),構(gòu)造函數(shù)本身并沒有提供額外的抽象能力,反倒增加了語法上的負(fù)擔(dān),因此,Rust推薦使用普通的靜態(tài)函數(shù)作為類型的構(gòu)造器,例如String::new()。對(duì)于那種無參數(shù)無錯(cuò)誤處理的簡單情況,標(biāo)準(zhǔn)庫提供了Default來做統(tǒng)一抽象,其定義如下:

pub trait Default: Sized {

fn default() -> Self;

}

屬性與derive

Rust中有一種語法,屬性(attribute),基本格式類似于#[xxx]。屬性可以用來注釋聲明,類似于Java中的注解。有一種非常實(shí)用的屬性derive,可以幫助我們自動(dòng)impl某些trait,因?yàn)閷?shí)現(xiàn)某些trait的時(shí)候,邏輯是非常機(jī)械化的,例如Debug。示例如下:

#[derive(Debug)]

struct Rectangle {

width: u32,

height: u32,

}

fn main() {

let rect = Rectangle {

width: 10,

height: 20,

};

println!("{:?}", rect); // 輸出:Rectangle { width: 10, height: 20 }

}

目前,Rust支持的可以自動(dòng)derive的trait有以下這些:

Debug Clone Copy Hash RustcEncodable RustcDecodable PartialEq Eq

ParialOrd Ord Default FromPrimitive Send Sync

參考文獻(xiàn)

《Rust編程之道》張漢東

《深入淺出Rust》范長春

總結(jié)

以上是生活随笔為你收集整理的corutine rust_Rust学习笔记#5:函数和trait的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。