目錄c++
rust是什麼?rust是一門新型的編程語言,目的是取代c/c++作爲一個系統開發級別的編程語言。它有着c的底層高效,有着c++的現代描述能力,同時保持足夠的潔簡。做爲一個新語言,他在吸收現代語言精華的同時,也避免其中設計中的一些繁瑣之處,同時它已經被許多大型的開發項目所採用,有着良好的商業化前景。express
rust屬於靜態類型語言。即編譯時必須知道全部變量的類型。編程
let 模式:數據類型 = 表達式; //定義不變量 let mut 模式:數據類型 = 表達式; //定義變量
長度 | 有符號 | 無符號 |
---|---|---|
8-bit | i8 | u8 |
16-bit | i16 | u16 |
32-bit | i32 | u32 |
64-bit | i64 | u64 |
架構 | isize | usize |
長度 | 名稱 |
---|---|
32-bit | f32 |
64-bit | f64 |
採用IEEE-754標準表示。數組
類型 | 真值 | 假值 |
---|---|---|
bool | true | false |
類型 | 值樣例 |
---|---|
char | 'A' |
字符是unicode標準編碼的標量值。安全
將多個數據類型組合成一個新的類型。bash
類型樣例 | 值樣例 |
---|---|
(i32,char,u8) | (500,'B',1) |
模式解構元組:數據結構
let (x,y,z)=(1,'A',3);
多線程
類型樣例 | 值樣例 |
---|---|
[i64;5] | [1,2,3,4,5] |
數組是固定長度,且元素類型相同。閉包
類型樣例 | 值樣例 |
---|---|
struct User{字段序列} | User{鍵值對序列} |
struct User{age:u8,active:bool} | User{age:18,active:true} |
struct User(u8,bool); | User(18,true) |
struct User; | User |
類型樣例 | 值樣例 |
---|---|
enum 名稱{類結構序列} | enum Message{Quit,Move{x:u8,y:u8},Write(u8,u8)} |
對數組的某個片斷創建投影視圖的類型。切片是動態大小類型,所以只能使用切片引用。架構
類型樣例 | 值樣例 |
---|---|
str | let a:&str = &"hello"[2..4]; |
[T] | let a:&[i8] = &[1,2,3][0..3]; |
引用是指針(保存地址),指向具體數據。在rust中,引用是沒有數據全部權,而只是」借用「使用權的特殊類型。
類型樣例 | 值樣例 |
---|---|
&類型 | let i :&int= &3; let j :int=*i; |
智能指針指的是能自動釋放相關資源的一系列數據結構。它表現形式相似指針,但大多數狀況都擁有數據全部權,所以概念上和rust引用不相干。
常見智能指針:
用途:嵌套類型、大數據轉移全部權、特徵接口。
智能指針通常須要實現:解引用特徵和析構特徵。
解引用特徵:
use std::ops::Deref;//解不可變引用 // DerefMut //解可變引用 struct MyBox<T>(T); impl<T> Deref for MyBox<T>{ type Target = T; fn deref(&self)->&T{ &self.0 } } let a = Mybox(3); let b = *a;// *a = *(a.deref()) = *(&T) = 3 //傳參時會自動解引用強制多態deref coercions。 fn f(x:&i32){println!("x={}",*x);} f(&a); // &a = &(*a) = &(3)
析構特徵:
impl<T> Drop for MyBox<T>{ fn drop(&mut self){ println!("自動釋放資源。"); } } fn main(){ let c = MyBox(3); drop(c);//手動釋放 }
rust經過引用來實現move的補充「借用」,同時用規則和編譯檢查來保證引用的安全性。可是某些時候仍是須要相似c語言的原生指針。
let mut a=3; let b = &a as *const i32; //指向不可變數據 let c = &mut a as *mut i32;//指向可變數據 let d = 0x234usize; let e = d as *const i32;//指向指定地址。 unsafe{//只能在不安全塊中解引用,獲得指向的數據 println!("*b={},*e={}", *b, *e); }
fn fa(f:fn(i32)->i32, a:i32)->i32{ f(a) + f(a) //經過函數指針f調用函數 } fn fb(x:i32)->i32{ x + x } let a = fa(fb, 5);//a=20;
rust 特殊用途的類型。
//別名類型。可命名的等價類型 type Myint = i32; //從不返回類型never type:"!" fn bar() ->!{ loop{} } //動態大小類型:str、特徵 let s:str = "hi"; //error.沒法肯定str類型靜態大小 let s:&str = "hi"; //&str 由指針和長度組成,大小等於 usize*2 let s:&std::fmt::Display = &"hi"; //str實現了Display特徵,而特徵是動態大小,因此只能創建特徵的引用 //用於肯定範型參數大小的特徵:sized fn f<T:sized>(x:T){} //sized說明T是有靜態大小的,sized可省略,自動添加 fn f2<T:?sized>(x:&T){}//?sized表示sized可選,於是x只能是引用類型&T。
rust經過模式匹配來實現靈活的判斷和定義變量。
模式種類:
let a=3; //模式a必然匹配 if let Some(b)=None{}; //模式Some(b)能夠不匹配 match a{ 1 => ,//字面值模式 1 | 2 => , //1 或 2 4...9 => , //4 到 9 3 if 3<2 =>, //匹配守衛,添加更多判斷 _ => ,//必然匹配佔位符 } //對複合結構的解構匹配 struct Point{ x:i32, y:i32, } let c = Point{x:1,y:2}; let Point{x,y} = c; //解構出x=1, y=2兩個變量 let &Point{x:x2,y:y2}= &c;//對引用進行解構,獲得x2,y2 let (_,d,..)=(1,2,3,4,5); //解構元組,忽略第一個值,得d=2,忽略剩餘值 if let e@1...5 = d {println!("e={}",e)};//@綁定匹配的值
格式 | 範例 |
---|---|
fn 函數名(參數列表)->返回類型{函數體} | fn main(){} |
函數頭部由函數名、參數列表和返回類型組成,參數列表是由逗號分隔的一系列變量組成;函數體由一系列語句(statements)和一個可選的表達式(expressions)結尾組成。
語句沒有返回值,以分號結尾。表達式有返回值,沒有分號結尾。
格式 | 範例 |
---|---|
if 布爾表達式 {真值語句體} else {假值語句體} | if 5>4 {} else {} |
if分支結構只會執行其中一條分支。if 分支結構是一個表達式。
格式 | 範例 |
---|---|
match 變量{模式=>返回值,...} | match m{Message::Quit=>1,Message::Write(x,y)=>x+y,Message::Move=>0} |
模式匹配必須窮盡全部狀況。「_」下劃線做爲模式表示匹配全部。
格式 | 範例 |
---|---|
if let 模式=變量{...} | if Message::Quit=m{1} |
if let匹配指定模式,忽略其餘狀況。
格式 | 範例 |
---|---|
loop{循環語句體} | loop{} |
while 布爾表達式{循環語句體} | while 5>4 {} |
while let 模式{循環體} | while let Some(t)= s.pop(){} |
for 元素 in 集合{循環語句體} | for i in [1,2].iter(){} |
impl塊能夠將函數或方法關聯到結構struct、枚舉enum。
格式 | 範例 |
---|---|
impl 結構名{函數列表} | impl User{fn hello(&self){}} |
取代特定類型的佔位符。經過特徵trait描述佔位符具有的通用能力。
rust範型支持特例化技術。即對特定類型能定義不一樣的操做邏輯。
範型最終會單態化monomorphization,即編譯爲特定類型的代碼,所以不會有性能的開銷,但會產生多份代碼的數據佔用。
格式 | 範例 |
---|---|
基礎結構 名稱 <佔位符列表> ... | fn f<T,U>(x:T,y:U){} |
trait Add<RHS=Self>{ //RHS默認類型是對象類型自身 type Output; fn add(self,rhs:RHS)->Self::Output; } struct A(i32); impl Add for A{ type Output = i32; fn add(self, r:A)->i32{self.0 + r.0} } let a = A(3); let b = A(4); println!("a+b={}", a.add(b));//a+b=7;
trait 相似接口。
特徵定義:
格式 | 範例 |
---|---|
trait 名稱{聲明序列} | trait s{fn t();} |
實現特徵:
格式 | 範例 |
---|---|
impl 特徵 for 結構{實現序列} | impl s for m{fn t(){}} |
變量 :impl 特徵 | fn f(t :impl s){} |
範型佔位符 : 特徵 | fn f
|
同上 | fn f
|
關聯類型只容許一個實現,而範型能夠有n個實現。
trait IA{ type Item;//關聯類型 fn print(&self)->Self::Item; } struct A; impl IA for A{ type Item = i32; fn print(&self)->Self::Item{3} } let a = A; let b = a.print(); //b = 3i32
特徵對象是rust面向對象技術的基礎之一,實現同一接口多態調用。
特徵對象要求:
格式 | 範例 |
---|---|
&特徵 | let a:&Drop = &"3".to_string(); |
閉包是能夠存儲到變量,捕獲調用者做用域的值的匿名函數特徵。
格式 | 範例 |
---|---|
|參數|{} |
let expr = |n|{n*2}; |
fn fa()=>Box<Fn()->i32>{ let a = 3;//局部變量 Box::new(move||a) //閉包捕獲a,但fa返回閉包,所以閉包生命週期比fa更長,默認捕獲的a無效(經過引用),move關鍵字將a全部權轉移到閉包。 //Fn閉包是一種特徵,所以無大小,需用智能指針Box包裝 } let a = fa()(); //a=3
trait Iterator{ type Item; fn next(&mut self) ->Option<Self::Item>; }
迭代器 | 做用 |
---|---|
into_iter() | 全部權迭代器 |
iter() | 可變引用迭代器,引用元素 |
iter_mut() | 可變引用迭代器,可變引用元素 |
rust語言爲了高效管理內存,使用全部權概念取代垃圾回收機制和手工管理內存。
棧stack和堆heap是內存的兩個區域。棧存放做用域上下文的變量,而堆存放自由分配的變量(經過指針使用)。
rust的堆中變量賦值是移動move語義,而非淺拷貝shallow copy或深拷貝deep copy。
所以全部權的意義是:值的全部者只有一個。該全部者離開做用域即丟棄值。
引用類型對move語義進行了補充,它不得到全部權,而只有使用權。爲了保證數據有效性,編譯器對引用對象的生命週期進行嚴格的檢查和假定,默認函數沒法經過引用返回對象。
關於引用(&類型):
獲取值的三種方式:
方式 | 全部權 | 生命週期 | 相關特徵 | 關鍵字 |
---|---|---|---|---|
T | 轉移 | 自動 | FnOnce | move |
&T | 無 | 編譯檢查 | Fn | |
&mut T | 無 | 編譯檢查 | FnMut |
變量都有生命週期,按定義的順序在做用域末尾逆序終結。引用自身的生命週期必須短於所引用對象的生命週期,不然出現懸垂引用(指向無效對象的指針)。
默認在同一個函數內能夠經過借用檢查器borrow checker自動判斷,可是跨函數傳遞引用就沒法自動處理。由於函數可被任意上下文調用,因此沒法假定引用參數應該具有怎樣的生命週期。
生命週期註解是一種針對引用參數和擁有引用的複合結構與函數的範型佔位符。它能夠將幾個變量的生命週期進行關聯,聲明具有「合理一致」的生命週期,從而讓編譯器取得檢查引用有效性的必要信息。
所謂「合理一致」指的編譯器對關聯的參數和返回值作出合理假設。如:
對於結構變量:
格式 | 範例 |
---|---|
&'註解 類型 | fn f<'a>(x:&'a i32){} |
struct 結構<'佔位符>{引用成員類型註解} | struct s<'a>{p: &'a str,} |
fn 函數(註解參數列表)->註解返回類型{} | fn f<'a>(x:&'a){} |
程序生命週期:&'static | let s:&'static str="str"; |
let a = 3; let mut p =&a; let mut p2; let b = 4; p = &b; //error,p比b長壽,由於b在p以後定義,即b先於p終結 fn f<'a>(x:&'a isize,y:&'a isize)->&'a isize{ x } p2 = f(&a,&b);//error, p2比f(a,b)長壽。由於雖然a比p2長壽,但b比p2短命,所以f(a,b)=b, b<p2,即便實際返回的是a struct C<'a>(mut &'a isize); let mut c = C(&b); let d = 5; c.0 = &d;//error, c.0 < c,被註解字段必須比結構長壽 //生命週期子類型lifetime subtyping struct E<'e, 'c :'e>{c:&'e C<'c>} struct E2<'e, T :'e>{c:&'e T} //相似定義 let e = E2{c:&c}; fn fe(x:C)->&isize{ E{c:&x}.c.0 //ok, 雖然E是臨時變量,x是move進來的參數,但E的定義讓其擁有兩個格外生命週期:E::c 和C::0,其中E自身的生命週期對應臨時變量,E::c對應x,而C::0只是被註解省略,對應->& isize }
爲了編碼方便,如下狀況能夠省略註解:
現代語言相對傳統語言,我的認爲最大的便利在於有一個設計良好的包管理系統。如何組織代碼,是一門管理學問,將有助咱們軟件工程的建設。
./Cargo.toml 工程描述文件,能夠是一個項目,也能夠是多個項目的工做區。
[package] name="包名" version = "0.1.1"
項目一個到n個crate(箱)組成,一個crate對應一個.rs源文件。
./src/main.rs 項目根,0-n個
./src/lib.rs 項目根(庫),0-1個
./src/bin/* 項目根(非默認項目)
庫項目能夠被其餘項目導入使用。
通常同時須要修改工程描述文件,添加依賴:
#Cargo.toml [dependencies] mylib = { path = "../mylib"}
注意,當前rust語言有兩個版本,最新版的模塊系統已經進行了大改,請務必使用nightly每晚編譯版本rust。
crate能夠劃分模塊。一個crate能夠有1個到n個模塊。默認模塊名x對應x.rs,其中根爲固定的」crate「名。
例子:
# :: 外部模塊前綴 ./src/main.rs #crate 根模塊(隱藏) ./src/a.rs #crate::a ./src/b.rs #crate::b ./src/c.rs #crate::c ./src/c/d.rs #crate::c::d
權限規則:
源文件定義:
//main.rs -- crate mod a; //在根層即根同目錄找a.rs定義 mod b{ pub fn f2(); } //內聯,將隱藏b.rs文件 mod c; fn main(){ a::f(); b::f();//error。f不存在被隱藏 c::f();//error.私有 c::d::f(); //d 必須在c/mod.rs定義爲pub mod d; } fn f(){} //a.rs -- crate::a pub fn f(){} fn f2(){} //b.rs -- crate::b use c::d; //use使用的永遠是絕對路徑,等於crate::c::d,而不是crate::b::c::d。 fn f(){ d::f2(); //ok c::d::f2(); //由於use crate::c::d;,因此導入了指定crate的模塊信息到當前模塊,因此c::d::f2()不會被識別爲crate::b::c::d::f2() } //c.rs -- crate::c pub mod d; fn f(){ d::f2(); //使用相對路徑,等於crate::c::d::f2(); self::d::f2(); //等於上面 } //c/d.rs -- crate::c::d pub fn f(){ crate::a::f();//用絕對路徑::訪問其餘模塊 crate::a::f2();//error a不是d的父親,d不能訪問其私有成員 crate::b::f2();//訪問的是在main.rs中的定義 super::f();//等價crate::c::f(), ok super::super::f();//相對路徑訪問main.rs 的f() } pub fn f2(){}
運用策略:
Cargo.toml 設置:
[profile.release] panic = 'abort' #直接終止程序 #或者展開回溯unwinding
使用演示:
panic!("error message!");
enum Result<T,E>{ Ok(T), Err(E), } let f = File::open("hello.txt").expect("自動報錯。"); //.unwrap();自動報錯,使用內部信息 let mut s = String::new(); f.read_to_string(&mut s)?; //?自動向調用方返回錯誤對象。
let mut v:Vec<i64> = Vec![1,2,3]; v.push(5); let el :i64 = v[1]; let el2 :Option<i64> = v.get(1); for i in &mut v{ *i += 123; }
String
是Vec<u8>
的包裝。
let mut s = String::from("hello world."); s.push_str("man."); format!("{}-{}-{}", s,s,s) let c :char = s.chars()[1]; let c2 :u8 = s.bytes()[2]; let c3 :&str = &s[3..];
HashMap<K,V>
use std::colletions::HashMap; let mut m= HashMap::new(); m.insert(String::from("yes"),0); let v :Option<_> = m.get(&String::from("yes"));
基本的堆引用,具備全部權。
let a = Box::new(3);//3存放在堆上,a有全部權 let b:i32 = *a;//解引用,取得堆上值3
引用計數(多全部權)。
use std::rc::Rc; let a = Rc::new(3); let b = Rc::clone(&a);//引用計數+1 let c = Rc::clone(&a);//引用計數2
返回內部可變引用
let a =Rc::new(RefCell::new(3));//建立內部可變3 let b =Rc::clone(&a); let c =Rc::clone(&a); *a.borrow_mut()+=1; //返回內部可變引用
弱引用,沒全部權。可用於解決循環引用問題。
let a=Rc::new(3); let b:Weak<_>=Rc::downgrade(&a);//從Rc引用中建立弱引用 drop(a); //手動刪除a println!("b={}",b.upgrade().expect("沒法訪問b"));
//生成自動打印內部信息的特徵 #[derive(Debug)] struct A(&str, i8); println!("{:?}", A("hi", 3)); //生成等於、不等特徵 #[derive(PartialEq)] //Eq struct B; println!("B==B?{}", B==B); //生成有序特徵 #[derive(Ord,PartialOrd,Eq,PartialEq,Debug)] struct C(i8); println!("{:?}>{:?}?{}",C(3),C(1),C(3)>C(1)); //生成克隆特徵(要求字段實現克隆特徵纔有效果) #[derive(Clone)] //Copy struct D(i8); let mut a=D(3); let b = a.clone(); let pa = a.0 as *const i8; let pb = b.0 as *const i8; println!("&{:?}->&{:?}={}",pa,pb,b.0); //生成哈希特徵 #[derive(Hash)] struct E; //生成默認值特徵 #[derive(Default)] struct F;
並行處理,會面對以下問題:
thread::spawn()
use std::thread; let a = 3; let handle = thread::spawn(move||{ println!("新線程。a={}", a);//move轉移a的全部權 }); handle.join().unwrap();//主線程等待新線程執行完畢 //不然主線程執行完畢會自動關閉子線程
新興的一種協調併發處理的方式。易用,單擁有權。
use std::sync::mpsc;//多產單消multiple producer,single consumer use std::thread; let (tx,rx)=mpsc::channel(); thread::spawn(move ||{tx.send("hi".to_string()).unwrap();}); println!("{}",rx.recv().unwrap());//獲取「hi」
互斥器mutex(mutual exclusion):任意時刻,只容許一個線程訪問數據。多擁有權。
use std::sync::Mutex; use std::sync::Arc; //原子性Rc use std::thread; let a = Mutex::new(3); { let mut b :MutexGuard<_>= a.lock().unwrap();//獲取互斥器內部數據的智能指針 //該指針擁有鎖,並能自動釋放鎖 *b+=1; }//自動釋放鎖 println!("a={:?}",a);//可再次讀取數據 //多線程共享互斥器 let b = Arc::new(Mutex::new(3));//Arc是跨線程安全的Rc,即多全部權智能指針 let c = Arc::clone(&b);//增長計數 let handle1 = thread::spawn(move||{ let n = c.lock().unwrap();//共享同一個鎖 *n += 1; }); let d = Arc::clone(&b);//計數3 let handle2 = thread::spawn(move||{ let n = d.lock().unwrap();//共享同一個鎖 *n += 2; }); handle1.join().unwrap(); handle2.join().unwrap(); println!("b={}", *b.lock().unwrap());
#[cfg(test)] //只在配置是test編譯 mod tests{ use super::*; //導入所有外部模塊 #[test] //測試項目 fn exploration()->Result<(),String>{ assert!(3>2); panic!("測試失敗。"); Err(String::from("Result失敗")) } #[test] #[should_panic] //但願出現異常,不然不經過 fn g(){ assert!(3<2); } }
構建目錄:/tests/,每一個.rs做爲獨立crate構建。
use adder; //導入待測試模塊 #[test] fn g(){ assert!(2>3,"出錯"); }
面向對象:封裝並自行維護內部狀態的結構體,在相同接口下多態調用。
rust具備面向對象的設計能力,但不強制使用面向對象,由於封裝是須要代價的。不要在細顆粒度下使用面向對象,避免過分包裝。
多態技術 | 動態性 | 同構性 |
---|---|---|
範型 | 靜態 | 同構 |
特徵對象 | 動態 | 異構 |
面向對象接口設計建議:
一些命令行下的編譯管理工具: