目錄程序員
每一種語言都有它比較隱祕的點。rust也不例外。算法
沒法靜態肯定大小或對齊的類型。編程
特徵包含vtable,經過vtable訪問成員。切片是數組或Vec的一個視圖。數組
rust是一個快速變化的語言和編譯系統,建議用最新版,不然可能出現兼容性問題而沒法經過編譯。安全
rustup 管理工具鏈的版本,分別有三個通道:nightly、beta、stable。如上所述,建議同時安裝nightly版本,保持最新狀態。bash
wget -O- https://sh.rustup.rs |sh #下載安裝腳本並執行
安裝以後,就能夠用rustup命令來控制整個工具鏈的更新了。數據結構
rustup toolchain add nightly #安裝每夜版本的工具鏈 rustup component add rust-src #安裝源代碼 cargo +nightly install racer #用每夜版本的工具鏈安裝racer,一個代碼補全的工具。由於當前只支持每夜版本 rustup component add rls # 這是面向編輯器的一個輔助服務 rustup component add rust-analysis #分析工具 #vscode : 搜索插件rls安裝便可
struct 結構體是一種記錄類型。成員稱爲域field,有類型和名稱。也能夠沒有名稱,稱爲元組結構tuple strcut。 只有一個域的特殊狀況稱爲新類型newtype。一個域也沒有稱爲類單元結構unit-like struct。多線程
類別 | 域名稱 | 域個數 |
---|---|---|
通常結構 | 有 | >1 |
元組結構 | 無 | - |
新類型 | - | 1 |
類單元結構 | - | 0 |
枚舉和結構是不一樣的,枚舉是一個集合(相似c語言種的聯合union,變量的枚舉成員可選,而不是全體成員),而結構是一個記錄(成員一定存在)。閉包
做爲一個描述力比較強的語法對象,結構不少時候都在模擬成基礎類型,可是更多時候是具有和基礎類型不一樣的特徵,而須要由用戶來定製它的行爲。框架
基礎數據類型,引用類1(引用&T,字符串引用&str,切片引用&[T],特徵對象&T:strait,原生指針*T),元組(T2),數組[T 2;size],函數指針fn()默認都是copy;而結構、枚舉和大部分標準庫定義的類型(智能指針,String等)默認是move。
注意:
#[derive(Copy,Clone,Debug)] //模擬基礎數據類型的自動複製行爲,Debug 特徵是爲了打印輸出 struct A; let a = A; let b = a;//自動copy 而不是move println!("{:?}", a);//ok
rust的基本類型分類能夠如此安排:
特徵是一個動態大小類型,它的對象大小根據實現它的實體類型而定。這一點和別的語言中的接口有着本質的不一樣。rust定義的對象,必須在定義的時候即獲知大小,不然編譯錯誤。
可是,泛型定義除外,由於泛型的實際定義是延遲到使用該泛型時,即給出具體類型的時候。
或者,能夠用間接的方式,如引用特徵對象。
trait s{} fn foo(a:impl s)->impl s{a} //這裏impl s相等於泛型T:s,屬於泛型技術,根據具體類型單態化
rust的核心創新:
必須總體的理解他們之間的關係。
借用(引用)是一種怎樣的狀態?
例子:
操做 | 舊讀 | 舊寫 | 新讀 | 新寫 | 新副本 |
---|---|---|---|---|---|
copy | ✔ | ✔ | ✔ | ✔ | ✔ |
move | ✘ | ✘ | ✔ | ✔ | ✘ |
& | ✔ | ✔1 | ✔ | ✘ | ✘ |
&mut | ✔ | ✔1 | ✔ | ✔ | ✘ |
注1:舊寫後引用當即無效。
從上表格能夠看出,引用(借用)並不僅是產生一個讀寫指針,它同時跟蹤了引用的原始變從量是否有修改,若是修改,引用就無效。這有點相似迭代器,引用是對象的一個視圖。
通常性原則:能夠存在多個只讀引用,或者存在惟一可寫引用,且零個只讀引用。(共享不可變,可變不共享)
特殊補充(便利性規則):
能夠將可寫引用轉換爲只讀引用。該可寫引用只要不寫入或傳遞,rust會認爲只是只讀引用間的共享,不然,其餘引用自動失效(rust編譯器在不停的進化,其主要方向是注重實質多於形式上,提供更大的便利性)。
let mut a = 1; let mut rma = &mut a; let mut ra = &a;//error let ra = rma as &i32; //ok println!("*ra={}", ra);//ok println!("*rma={}", rma);//ok *rma = 2; //ra 失效 println!("*ra={}", ra);//error println!("*rma={}", rma);//ok a = 3; //ra、rma 都失效 println!("*ra={}", ra);//error println!("*rma={}", rma);//error
用途在哪裏?
let p1 = rma; //error let p2 = ra; //ok
即:須要產生多個引用,但不是傳遞函數參數的場合。如轉換到基類或特徵接口(如for循環要轉換到迭代器)。
let v=[1,2,3]; let p1=&mut v; let p2=p1 as &[i32]; // 註釋掉其中一組代碼 // 1 for item in p1{} println!("{}",p1);//error // 2 for item in p2{} println!("{}",p2);//ok
引用沒法移動指向的數據:
let a = String::from("hi"); let pa = &a; let b = *pa; //error
引用並無論理對象生命週期,由於它沒有對象的全部權。引用須要斷定的是引用的有效性,即對象生命週期長於引用自己便可。
當須要管理生命週期時,不該該使用引用,而應該用智能指針。
通常而言,咱們不喜歡引用,由於引用引入了更多概念,因此咱們但願智能指針這種東西來自動化管理全部資源。但不能否認,不少算法直接使用更底層的引用能提升效率。我的認爲:結構組織須要智能指針,算法內部可使用引用。
爲了處理異常狀況,rust經過Result<T,E>
定義了基礎的處理框架。
fn f1()->Result<(),Error>{ File::open("file")?; //?自動返回錯誤 OK(()) //正常狀況 } //main()函數怎麼處理? fn main()->Result<(),Error>{} pub trait Termination{ //返回類型須支持該特徵 fn report(self) -> i32; } impl Termination for (){ fn report(self) ->i32{ ExitCode::SUCCESS.report() } } impl<E: fmt::Debug> Termination for Result<(),E>{ fn report(self)->i32{ match self{ Ok(())=>().report(), Err(err)=>{ eprintln!("Error:{:?}",err); ExitCode::FAILURE.report() } } } }
rustup target add wasm-wasi #編譯目標爲wasm(網頁彙編) cargo build --target=wasm-wasi
解引用:
&T => &U => &K
自動轉換類型,直到找到該成員函數。// *p=>*(p.deref()) // =>*(&Self) // =>Self pub trait Deref { type Target: ?Sized; fn deref(&self) -> &Self::Target; } // *p=>*(p.deref_mut()) // =>*(&mut Self) // =>mut Self pub trait DerefMut: Deref { fn deref_mut(&mut self) -> &mut Self::Target; }
經常使用智能指針(管理堆內存上的數據):
擁有全部權:
Box<T>
=>T 堆上變量,對應普通棧上變量Vec<T>
=>[T] 序列String == Vec<u8> =>[u8]
知足utf8編碼共享全部權(模擬引用):
Rc<T>
=>T 共享,智能化的&,採用引用計數技術
Rc<T>::clone()
產生鏡像,並增長計數Rc<RefCell<T>>
模擬&mut,內部元素可寫Arc<T>
多線程共享無全部權:
Weak<T>
弱引用特殊用途:
Cell<T>
內部可寫RefCell<T>
內部可寫Cow<T>
Clone-on-WritePin<T>
防止自引用結構移動產生循環引用的條件(如:Rc<RefCell<T>>
):
設計遞歸型數據結構例子:
//遞歸型數據結構,須要使用引用 //引用是間接結構,不然該遞歸數據結構有無限大小 //其次,引用須要明確初始化,引用遞歸自身致使沒法初始化 //所以,須要Option來定義沒引用的狀況 //Node(Node) :無限大小 //=> Node(Box<Node>) :不能初始化 //=> Node(Option<Box<Node>>) :ok struct Node<T>{ next: Option<Box<Self>>, data:T, }
有些數據結構,一個節點有多個被引用的關係,好比雙向鏈表,這時只能用Option<Rc<RefCell<T>>>
這套方案,但存在循環引用的風險,程序員須要創建一套不產生循環引用的程序邏輯,如正向強引用,反向弱引用。
編譯器會對自引用(本身的成員引用本身,或另外一個成員)的狀況進行檢查,由於違反了移動語義。
pub trait FnOnce<Args> { type Output; extern "rust-call" fn call_once(self, args: Args) -> Self::Output; } pub trait FnMut<Args> : FnOnce<Args> { extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output; } pub trait Fn<Args> : FnMut<Args> { extern "rust-call" fn call(&self, args: Args) -> Self::Output; }
靜態分派:泛型
動態反派:特徵對象(指針)
// 只讀轉可寫引用: #[lang = "unsafe_cell"] #[stable(feature = "rust1", since = "1.0.0")] pub struct UnsafeCell<T: ?Sized> { value: T, } // 假類型(佔位類型) #[lang = "phantom_data"] #[stable(feature = "rust1", since = "1.0.0")] pub struct PhantomData<T:?Sized>; // 內存分配器 #[derive(Copy, Clone, Default, Debug)] pub struct Heap;
struct A; impl A{ fn f1(self){} fn f2(this:Self){} fn f3()->Self{A} } trait X{fn fx(self);} impl X for A{fn fx(self){}} //Self 表示實現的具體類型,本例爲A //self = self:Self //&self = self:&Self //&mut self = self:&mut Self let a = A; a.f1(); //ok 能夠用.調用成員方法 a.f2(); //error 不能夠,由於第一個參數名字不是self,雖然是Self類型 A::f2(a); //ok let a=A::f3(); //無參數構造函數的形式,名字隨意 impl i32{} //error ,i32不是當前項目開發的,不能添加默認特徵的實現,但能夠指定一個特徵來擴展i32 impl X for i32{} //ok
規則: 特徵和實現類型,必須有一個是在當前項目,也就是說不能對外部類型已經實現的特徵進行實現。也就是說,庫開發者應該提供完整的實現。
規則: 用小數點.調用成員函數時,編譯器會自動轉換類型爲self的類型(只要可能)。
可是要注意,&self -> self
是不容許的,由於引用不能移動指向的數據。能夠實現對應&T
的相同接口來解決這個問題。
let a = &A; a.fx(); //error 至關於調用fx(*a) impl X for &A{ fn fx(self){} //這裏的self = self:&A } a.fx(); //ok
容器 | 說明 |
---|---|
Vec | 序列 |
VecDeque | 雙向隊列 |
LinkedList | 雙向鏈表 |
HashMap | 哈希表 |
BTreeMap | B樹表 |
HashSet | 哈希集 |
BTreeSet | B樹集 |
BinaryHeap | 二叉堆 |
trait Iterator { type Item; fn next(&mut self) -> Option<Self::Item>; ... } //for 循環實際使用的迭代器 trait IntoIterator { type Item; type IntoIter: Iterator<Item=Self::Item>; fn into_iter(self) -> Self::IntoIter; }
容器生成三種迭代器:
·iter()
創造一個Item是&T類型的迭代器;·iter_mut()
創造一個Item是&mut T類型的迭代器;·into_iter()
根據調用的類型來創造對應的元素類型的迭代器。
適配器(運算完畢返回迭代器):
生成器(實驗性):
let mut g=||{loop{yield 1;}}; let mut f = ||{ match Pin::new(&mut g).resume(){ GeneratorState::Yielded(v)=>println!("{}", v), GeneratorState::Complete(_)=>{}, }; f(); //print 1 f(); //print 1
pub trait AsRef<T: ?Sized> { fn as_ref(&self) -> &T; } pub trait AsMut<T: ?Sized> { fn as_mut(&mut self) -> &mut T; } //要求hash不變 pub trait Borrow<Borrowed: ?Sized> { fn borrow(&self) -> &Borrowed; } pub trait From<T> { fn from(T) -> Self; } //標準庫已經有默認實現,即調用T::from pub trait Into<T> { fn into(self) -> T; } //克隆後轉換 pub trait ToOwned { type Owned: Borrow<Self>; fn to_owned(&self) -> Self::Owned; fn clone_into(&self, target: &mut Self::Owned) { ... } } //克隆寫入 pub enum Cow<'a, B> where B: 'a + ToOwned + ?Sized, { Borrowed(&'a B), Owned(<B as ToOwned>::Owned), } pub trait ToString { fn to_string(&self) -> String; } pub trait FromStr { type Err; fn from_str(s: &str) -> Result<Self, Self::Err>; }
trait Add<RHS = Self> { type Output; fn add(self, rhs: RHS) -> Self::Output; }
平臺相關字符串:
文件讀寫:
標準輸入輸出:
std::any
啓動新線程框架代碼:
use std::thread; let child = thread::spawn(move ||{}); child.join(); //等待子線程結束
數據競爭三個條件:
數據同步框架:
lock()
的內部可變類型也是安全的lock()
包裝的。多線程下的數據總結:
T
:移動(或複製),於是沒法共享(也就是隻能給一個線程使用)&T
static T
,ok&mut T
static mut T
, 不安全報錯Box<T>
:普通智能指針沒有共享功能,等價T
Rc<T>
:普通共享指針沒有Send特徵,技術實現使用了內部可變,但沒有加線程鎖進行安全處理Arc<T>
提供了線程安全的共享指針Mutex<T>
提供了線程安全的可寫能力。
use std::sync::{Arc,Mutex}; use std::thread; const COUNT:u32=1000000; let a = Arc::new(Mutex::new(123));//線程安全版共享且內部可變 // 1 let c = a.clone(); let child1 = thread::spawn(move ||{for _ in 0..COUNT {*c.lock().unwrap()+=2;}}); // 2 let c = a.clone(); let child2 = thread::spawn(move ||{for _ in 0..COUNT {*c.lock().unwrap()-=1;}}); // 多任務同步 child1.join().ok(); child2.join().ok(); println!("final:{:?}", a);//1000123
模式匹配我以前沒什麼接觸,因此感受挺有意思的(因此有了這一節)。
在rust中,let,函數參數,for循環,if let,while let,match等位置其實是一個模式匹配的過程,而不是其餘語言中普通的定義變量。
let x = 1; //x 這個位置是一個模式匹配的過程
模式匹配會有兩種結果,一種是不匹配,一種是匹配。一個模式匹配位置,要求不能存在不匹配的狀況,這種叫必然匹配「irrefutable」,用於定義。不然,用於分支判斷。
很明顯定義變量必然是要求匹配的,如let,函數參數,for循環。而用於分支判斷的是if let,wdhile let,match。
那爲何要用模式匹配來定義變量?由於很靈活,看一下它的表達能力:
x --> T
&x --> &T
得 x=T[_,_,x,_,_] --> [T;5]
得 x= 第三個元素(..,x) --> (1,2,"x")
得 x= 第三個成員T{0:_,1:x} --> T(1,"x")
得 x= 第二個成員經過與目標類型差很少的寫法,對複雜類型進行解構,相對直觀。
另外一方面,用於判斷的模式匹配能夠針對動態的內容,而不僅是類型來進行匹配,如:
//演示代碼 let [_,_,x,_,_] = [1;5]; let (..,x) = (1,2,'y'); let hi = &['h','e','l','l','o'][2..4]; struct Ax(i32,char); let Ax{1:_,0:x} = Ax(1,'x'); if let x@['l',_]=hi {println!("{:?}", x)}; if let x@1...10 = 7 {println!("{}",x)};