選擇 rust 的理由
- Rust 有助于您提供代碼質量
- 讓我們更加明確地了解性能成本
- 便于開發(fā)人員權衡代碼性能利弊
- Rust 更加關注代碼的質量和正確性
- 強調(diào)內(nèi)存安全,除非指定了"unsafe"
- 強大的類型系統(tǒng)咬扇、匹配系統(tǒng)
- 寫 Rust 有一種寫 kotlin 或者 go 這些高級語言的感覺
特征
組成我們應用通常是兩個部分數(shù)據(jù)和行為鹃共,我們編程多半工作就是用行為操作數(shù)據(jù)。
fn make_true(input:&str) -> String{
format!("{}!!",input)
}
定義函數(shù) make_true 對字符串進行操作松靡,然后返回字符串滞详,這是對數(shù)據(jù)的操作。
#[derive(Debug)]
struct Fact {
text: String
}
fn make_true(input:&Fact) -> Fact{
Fact{text:format!("{}!!",input.text)}
}
?往往將字符串定義在范圍箩言,我們將字符串作為對象 Fact 的屬性,然后 make_true 接收 Fact 對象然后對其 text 屬性進行操作焕襟。
我們希望方法與數(shù)據(jù)有一定關系陨收,也就是方法屬于數(shù)據(jù),或者說想要將方法添加到數(shù)據(jù)上鸵赖,在其他語言是 method务漩。在 rust 為結構體添加行為很簡單,
- 通過關鍵字 impl 其后添加要添加方法到結構體名稱
- 然后寫實現(xiàn)
impl Fact {
fn make_true(&self) -> Fact{
Fact{text:format!("{}!!",self.text)}
}
}
fn main(){
let fact = Fact{text:String::from("hello")};
println!("{:#?}",fact.make_true());
}
上面代碼大家疑問最多可能就是 self它褪,為什么我們需要 self
- 通過 self 方法可以訪問到數(shù)據(jù)
- 所以結構體內(nèi)實現(xiàn)方法饵骨,都會得到數(shù)據(jù)引用作為方法第一個參數(shù),然后來通過 self 引用來訪問數(shù)據(jù)茫打,類似 javascript 和 java 中的 this居触。
根據(jù)需要可以指定不同 self -
&self
借用滨砍、只讀版本 -
&mut self
可變借用版本 -
[mut] self
: 所有版本方妖,可以操作修改 self
impl Fact {
fn make_true(&mut self){
self.text.push_str("!!");
}
}
impl Fact {
fn make_true(mut self) -> Fact{
self.text.push_str("!!");
self
}
}
let fact = Fact{text:String::from("hello")};
let fact_1 = fact.make_true();
println!("{:#?}",fact_1.text);
特征定義
trait 用于定義與其他類型共享功能旺韭,這是一種抽象钙态,類似于其他語言(例如 go 語言)中的接口些膨。今天將介紹如何創(chuàng)建 trait 以及其實現(xiàn)和使用渗磅,最后會給出基于 trait 實現(xiàn)日志系統(tǒng)翼岁。
定義 trait
抽象方式定義共享的行為
pub trait GetInformation {
fn get_title(&self)-> &String;
fn get_course(&self)-> u32;
}
使用 trait 關鍵字來定義特征楼肪,然后在其中定義一系列行為开财,也就是空方法汉柒,需要結構體去實現(xiàn)。
pub struct Tut {
pub title: String,
pub course: u32,
}
impl GetInformation for Tut {
fn get_title(&self) -> &String{
&self.title
}
// u32 天然具有 copy 特征
fn get_course(&self) -> u32{
self.course
}
}
定義結構體實現(xiàn)特征 GetInformation 的 get_title 這個結構體就具有 GetInfomation 特征责鳍。
fn main() {
let js_tut = Tut{title:"vue".to_string(),course:10};
println!("js_tut title = {} course = {}",js_tut.get_title(),js_tut.get_course())
}
在 go 語言我們可以根據(jù)行為進行劃分類別竭翠,只要具有行為結構體就屬于某一個按類別進行劃分的類別。有時候我們傳入結構體薇搁,需要具有一定能力斋扰。
實現(xiàn) trait
作為參數(shù)輸入結構體需要具有一定行為,也就是結構體實現(xiàn)某種特征
fn print_information(tut:impl GetInformation){
println!("title = {}",tut.get_title());
println!("age = {}",tut.get_course())
}
fn main() {
let js_tut = Tut{title:"vue".to_string(),course:10};
// println!("js_tut title = {} course = {}",js_tut.get_title(),js_tut.get_course())
print_information(js_tut);
}
默認 Trait 實現(xiàn)
有些時候我們可以通過給出默認方法的實現(xiàn)啃洋,也就是給方法提供默認行為传货。
trait TutInfo {
fn get_tut_info(&self) -> String{
String::from("supplied by zidea zone")
}
}
定義 TutInfo 特征,然后給行為定義默認行為宏娄,如果實現(xiàn)特征 TutInfo 沒有復寫 get_tut_info 方法時就會默認執(zhí)行 TutInfo 特征默認提供的行為问裕。
impl TutInfo for Tut {
// add code here
}
let js_tut_info = js_tut.get_tut_info();
println!("tut info = {}",js_tut_info);
Trait 邊界
trait 邊界指定泛型是任何擁有特定行為的類型。
fn print_information_two<T:GetInformation>(tut:T){
println!("title = {}",tut.get_title());
println!("age = {}",tut.get_course());
}
這里就是使用特征邊界(trait bound)來定義函數(shù)接受參數(shù)需要具有一定約束(要求結構體必須實現(xiàn)某種方法)孵坚,所以約束就是要求結構體需要具有一定能力粮宛。
也可以指定多個特征邊界(train_bound), 來約束對象具有多個行為窥淆。其實語法還是比較好理解,一門新語言帶來很多新的特性巍杈,但是并不能改變你編程和設計程序能力忧饭。只是便于你對程序設計的實現(xiàn)。
trait GetTitle {
fn get_title(&self) -> &String;
}
trait GetCourse {
fn get_course(&self) -> u32;
}
impl GetTitle for Tut {
fn get_title(&self)->&String{
&self.title
}
}
impl GetCourse for Tut {
fn get_course(&self) -> u32{
self.course
}
}
fn print_information_three<T:GetTitle+GetCourse>(tut:T){
println!("title = {}",tut.get_title());
println!("age = {}",tut.get_course());
}
還有一種特征邊界的寫法筷畦,通過 where 對泛型進行限制词裤,
fn print_information_five<T>(tut:T) where T:GetCourse + GetTitle{
println!("title = {}",tut.get_title());
println!("age = {}",tut.get_course());
}
接下來,也可以特征邊界( trait bound) 來約束函數(shù)的返回值類型鳖宾。
fn get_tut() -> impl GetTitle {
Tut{
title:String::from("react"),
course:20
}
}
這里需要補充一下這里 get_tut 返回值是 trait 類型吼砂,我們不能通過條件來返回不同都實現(xiàn) GetTitle 的不同結構體,這樣會報錯
let reactTut = get_tut();
println!("title of react tut = {}",reactTut.get_title());
Trait 對象
在我們所熟悉的面向對象編程語言中鼎文,對象包含數(shù)據(jù)和行為渔肩。創(chuàng)建對象便可調(diào)用其方法(行為)進行操作。在 rust 我們將數(shù)據(jù)保存在 enums 或是 structs ,而行為寫作 trait 里拇惋,也就是將數(shù)據(jù)和行為分開赖瞒。Trait 對象行為更像傳統(tǒng)的對象。Trait 對象可以包含數(shù)據(jù)和行為蚤假,但是又不同于傳統(tǒng)對象栏饮。
- 系統(tǒng)日志信息
- 日志需要可配置
- 在開發(fā)版提供詳細信息,而在發(fā)布版提供簡要的信息
定義 Logger 結構體磷仰,添加輸出不同級別日志信息
#[derive(Debug)]
struct Loggger {
}
impl Logger{
fn error(&self, message:&str){}
fn warn(&self, message:&str){}
fn info(&self, message:&str){}
fn debug(&self, message:&str){}
}
上面定義了不同級別的日志輸出 error, warn, info 和 debug 級別日志輸出
根據(jù)日志輸出形式又定義不同類型日志輸出
- FilerLogger
- PrintLogger
- NullLogger
- ExternalServiceLoagger
trait Loggger {
fn error(&self, message:&str);
fn warn(&self, message:&str);
fn info(&self, message:&str);
fn debug(&self, message:&str);
}
#[derive(Debug)]
struct PrintLogger {
}
impl Loggger for PrintLogger {
fn error(&self, message:&str){
println!("ERROR:{} ",message)
}
fn warn(&self, message:&str){
println!("ERROR:{} ",message)
}
fn info(&self, message:&str){
println!("ERROR:{} ",message)
}
fn debug(&self, message:&str){
println!("ERROR:{} ",message)
}
}
?這里我們定義特征 Logger袍嬉,讓不同日志輸出類型都去實現(xiàn) Logger 特征,這樣他們就是不同類型卻具有相同行為的類別
pub fn log_example(logger:&Logger){
logger.info("runing example code ..");
logger.info("done example code");
}
這里 &Logger
也稱為Trait 對象灶平,在 Trait 對象中包含
- 一個指向一個基本類型(可能是 PrintLogger 也可能是 FileLogger)
-
指針指向虛擬方法表(vtable)
這些都是由編譯器實現(xiàn)的
Trait 對象
在 Trait 對象是包含一個指向堆上數(shù)據(jù)指針伺通,以這種形式來保存數(shù)據(jù),當堆上值大小變化不會影響到 Trait 對象逢享,這樣更利于分配內(nèi)存給對象罐监。
logger.info("runing example code ..");
在上面語句編譯器會先從 vtable 中加載 info 函數(shù)地址,然調(diào)用到 info 函數(shù)的地址瞒爬」看整個過程,可能會擔心效率侧但,這樣做會不會影響程序的性能矢空。