鍍金池/ 教程/ Java/ 復合類型
標準輸入與輸出
消息傳遞
循環(huán)
注釋
Rust for Mac OS
幾種智能指針
Cell, RefCell
trait對象 (trait object)
rust web 開發(fā)
Unsafe、原始指針
Macro
迭代器
函數(shù)
Borrow, BorrowMut, ToOwned
快速上手
二叉樹
編輯器
測試與評測
Deref
安裝Rust
哈希表 HashMap
原生類型
17.錯誤處理
VS Code 安裝配置
動態(tài)數(shù)組Vec
模式匹配
操作符和格式化字符串
Rust for Linux
函數(shù)參數(shù)
Visual Studio
vim/GVim安裝配置
閉包作為參數(shù)和返回值
安全(Safety)
Cow
生命周期( Lifetime )
閉包的實現(xiàn)
所有權(Ownership)
Atom
將Rust編譯成庫
類型、運算符和字符串
類型系統(tǒng)中的幾個常見 trait
特性
屬性和編譯器參數(shù)
Spacemacs
集合類型
Rust json處理
Heap & Stack
并行
標準庫示例
基本程序結(jié)構(gòu)
鏈表
trait 和 trait對象
前期準備
代碼風格
編譯器參數(shù)
基于語義化版本的項目版本聲明與管理
Rust 版本管理工具: rustup
引用&借用(References&Borrowing)
注釋與文檔
10.1 trait關鍵字
模式
調(diào)用ffi函數(shù)
unsafe
并發(fā),并行,多線程編程
AsRef 和 AsMut
Rust旅程
Rust for Windows
結(jié)構(gòu)體與枚舉
條件分支
附錄I-術語表
變量綁定與原生類型
Mutex 與 RwLock
泛型
裸指針
常用數(shù)據(jù)結(jié)構(gòu)實現(xiàn)
系統(tǒng)命令:調(diào)用grep
Into/From 及其在 String 和 &str 互轉(zhuǎn)上的應用
共享內(nèi)存
Sublime
網(wǎng)絡模塊:W貓的回音
函數(shù)返回值
包和模塊
高階函數(shù)
函數(shù)與方法
match關鍵字
隊列
目錄操作:簡單grep
語句和表達式
并發(fā)編程
閉包
測試
閉包的語法
同步
迭代器
String
Send 和 Sync
Rc 和 Arc
屬性
Emacs
優(yōu)先隊列
Prelude
cargo簡介
控制流(control flow)
數(shù)組、動態(tài)數(shù)組和字符串
FFI
模塊和包系統(tǒng)、Prelude
實戰(zhàn)篇
Rust 是一門系統(tǒng)級編程語言,被設計為保證內(nèi)存和線程安全,并防止段錯誤。作為系統(tǒng)級編程語言,它的基本理念是 “零開銷抽象”。理
運算符重載
Any和反射
rust數(shù)據(jù)庫操作
輸入輸出流
復合類型
性能測試

復合類型

元組(Tuple)

在別的語言里,你可能聽過元組這個詞,它表示一個大小、類型固定的有序數(shù)據(jù)組。在 Rust 中,情況并沒有什么本質(zhì)上的不同。不過 Rust 為我們提供了一系列簡單便利的語法來讓我們能更好的使用他。

let y = (2, "hello world");
let x: (i32, &str) = (3, "world hello");

// 然后呢,你能用很簡單的方式去訪問他們:

// 用 let 表達式
let (w, z) = y; // w=2, z="hello world"

// 用下標

let f = x.0; // f = 3
let e = x.1; // e = "world hello"

結(jié)構(gòu)體(struct)

在Rust中,結(jié)構(gòu)體是一個跟 tuple 類似 的概念。我們同樣可以將一些常用的數(shù)據(jù)、屬性聚合在一起,就形成了一個結(jié)構(gòu)體。

所不同的是,Rust的結(jié)構(gòu)體有三種最基本的形式。

具名結(jié)構(gòu)體

這種結(jié)構(gòu)體呢,他可以大致看成這樣的一個聲明形式:

struct A {
    attr1: i32,
    atrr2: String,
}

內(nèi)部每個成員都有自己的名字和類型。

元組類型結(jié)構(gòu)體

元組類型結(jié)構(gòu)體使用小括號,類似 tuple 。

struct B(i32, u16, bool);

它可以看作是一個有名字的元組,具體使用方法和一般的元組基本類似。

空結(jié)構(gòu)體

結(jié)構(gòu)體內(nèi)部也可以沒有任何成員。

struct D;

空結(jié)構(gòu)體的內(nèi)存占用為0。但是我們依然可以針對這樣的類型實現(xiàn)它的“成員函數(shù)”。

不過到目前為止,在 1.9 版本之前的版本,空結(jié)構(gòu)體后面不能加大括號。 如果這么寫,則會導致這部分的老編譯器編譯錯誤:

struct C {

}

實現(xiàn)結(jié)構(gòu)體(impl)

Rust沒有繼承,它和Golang不約而同的選擇了trait(Golang叫Interface)作為其實現(xiàn)多態(tài)的基礎??墒?,如果我們要想對一個結(jié)構(gòu)體寫一些專門的成員函數(shù)那應該怎么寫呢?

答: impl

talk is cheap ,舉個栗子:

struct Person {
    name: String,
}

impl Person {
    fn new(n: &str) -> Person {
        Person {
            name: n.to_string(),
        }
    }

    fn greeting(&self) {
        println!("{} say hello .", self.name);
    }
}

fn main() {
    let peter = Person::new("Peter");
    peter.greeting();
}

看見了 self,Python程序員不厚道的笑了。

我們來分析一下,上面的impl中,new 被 Person 這個結(jié)構(gòu)體自身所調(diào)用,其特征是 :: 的調(diào)用,Java程序員站出來了:類函數(shù)! 而帶有 selfgreeting ,更像是一個成員函數(shù)。

恩,回答正確,然而不加分。

關于各種ref的討論

Rust 對代碼有著嚴格的安全控制,因此對一個變量也就有了所有權和借用的概念。所有權同一時間只能一人持有,可變引用也只能同時被一個實例持有,不可變引用則可以被多個實例持有。同時所有權能被轉(zhuǎn)移,在Rust中被稱為 move 。

以上是所有權的基本概念,事實上,在整個軟件的運行周期內(nèi),所有權的轉(zhuǎn)換是一件極其惱人和煩瑣的事情,尤其對那些初學 Rust 的同學來說。同樣的,Rust 的結(jié)構(gòu)體作為其類型系統(tǒng)的基石,也有著比較嚴格的所有權控制限制。具體來說,關于結(jié)構(gòu)體的所有權,有兩種你需要考慮的情況。

字段的 ref 和 owner

在以上的結(jié)構(gòu)體中,我們定義了不少結(jié)構(gòu)體,但是如你所見,結(jié)構(gòu)體的每個字段都是完整的屬于自己的。也就是說,每個字段的 owner 都是這個結(jié)構(gòu)體。每個字段的生命周期最終都不會超過這個結(jié)構(gòu)體。

但是有些時候,我只是想要持有一個(可變)引用的值怎么辦? 如下代碼:

struct RefBoy {
    loc: &i32,
}

這時候你會得到一個編譯錯誤:

<anon>:6:14: 6:19 error: missing lifetime specifier [E0106]
<anon>:6         loc: & i32,

這種時候,你將持有一個值的引用,因為它本身的生命周期在這個結(jié)構(gòu)體之外,所以對這個結(jié)構(gòu)體而言,它無法準確的判斷獲知這個引用的生命周期,這在 Rust 編譯器而言是不被接受的。 因此,這個時候就需要我們給這個結(jié)構(gòu)體人為的寫上一個生命周期,并顯式地表明這個引用的生命周期。寫法如下:

struct RefBoy<'a> {
    loc: &'a i32,
}

這里解釋一下這個符號 <>,它表示的是一個 屬于 的關系,無論其中描述的是 生命周期 還是 泛型 。即: RefBoy in 'a。最終我們可以得出個結(jié)論,RefBoy 這個結(jié)構(gòu)體,其生命周期一定不能比 'a 更長才行。

寫到這里,可能有的人還是對生命周期比較迷糊,不明白其中緣由,其實你只需要知道兩點即可:

  1. 結(jié)構(gòu)體里的引用字段必須要有顯式的生命周期
  2. 一個被顯式寫出生命周期的結(jié)構(gòu)體,其自身的生命周期一定小于等于其顯式寫出的任意一個生命周期

關于第二點,其實生命周期是可以寫多個的,用 , 分隔。

注:生命周期和泛型都寫在 <> 里,先生命周期后泛型,用,分隔。

impl中的三種self

前面我們知道,Rust中,通過impl可以對一個結(jié)構(gòu)體添加成員方法。同時我們也看到了self這樣的關鍵字,同時,這個self也有好幾種需要你仔細記憶的情況。

impl中的self,常見的有三種形式:self、 &self、&mut self ,我們分別來說。

被move的self

正如上面例子中的impl,我們實現(xiàn)了一個以 self 為第一個參數(shù)的函數(shù),但是這樣的函數(shù)實際上是有問題的。 問題在于Rust的所有權轉(zhuǎn)移機制。

我曾經(jīng)見過一個關于Rust的笑話:"你調(diào)用了一下別人,然后你就不屬于你了"。

比如下面代碼就會報出一個錯誤:

struct A {
    a: i32,
}
impl A {
    pub fn show(self) {
        println!("{}", self.a);
    }
}

fn main() {
    let ast = A{a: 12i32};
    ast.show();
    println!("{}", ast.a);
}

錯誤:

13:25 error: use of moved value: `ast.a` [E0382]
<anon>:13     println!("{}", ast.a);

為什么呢?因為 Rust 本身,在你調(diào)用一個函數(shù)的時候,如果傳入的不是一個引用,那么無疑,這個參數(shù)將被這個函數(shù)吃掉,即其 owner 將被 move 到這個函數(shù)的參數(shù)上。同理,impl 中的 self ,如果你寫的不是一個引用的話,也是會被默認的 move 掉喲!

那么如何避免這種情況呢?答案是 CopyClone

#[derive(Copy, Clone)]
struct A {
    a: i32,
}

這么寫的話,會使編譯通過。但是這么寫實際上也是有其缺陷的。其缺陷就是: Copy 或者 Clone ,都會帶來一定的運行時開銷!事實上,被move的 self 其實是相對少用的一種情況,更多的時候,我們需要的是 refref mut 。

ref 和 ref mut

關于 refmut ref 的寫法和被 move 的 self 寫法類似,只不過多了一個引用修飾符號,上面有例子,不多說。

需要注意的一點是,你不能在一個 &self 的方法里調(diào)用一個 &mut ref ,任何情況下都不行!

但是,反過來是可以的。代碼如下:

#[derive(Copy, Clone)]
struct A {
    a: i32,
}
impl A {
    pub fn show(&self) {
        println!("{}", self.a);
        // compile error: cannot borrow immutable borrowed content `*self` as mutable
        // self.add_one();
    }
    pub fn add_two(&mut self) {
        self.add_one();
        self.add_one();
        self.show();
    }
    pub fn add_one(&mut self) {
        self.a += 1;
    }
}

fn main() {
    let mut ast = A{a: 12i32};
    ast.show();
    ast.add_two();
}

需要注意的是,一旦你的結(jié)構(gòu)體持有一個可變引用,你,只能在 &mut self 的實現(xiàn)里去改變他!

Rust允許我們靈活的對一個 struct 進行你想要的實現(xiàn),在編程的自由度上無疑有了巨大的提高。

至于更高級的關于 trait 和泛型的用法,我們將在以后的章節(jié)進行詳細介紹。

枚舉類型 enum

Rust的枚舉(enum)類型,跟C語言的枚舉有點接近,然而更強大,事實上它是一種代數(shù)數(shù)據(jù)類型(Algebraic Data Type)。

比如說,這是一個代表東南西北四個方向的枚舉:

enum Direction {
    West,
    North,
    Sourth,
    East,
}

但是,rust 的枚舉能做到的,比 C 語言的更多。 比如,枚舉里面居然能包含一些你需要的,特定的數(shù)據(jù)信息! 這是常規(guī)的枚舉所無法做到的,更像枚舉類,不是么?

enum SpecialPoint {
    Point(i32, i32),
    Special(String),
}

你還可以給里面的字段命名,如

enum SpecialPoint {
    Point {
        x: i32,
        y: i32,
    },
    Special(String),
}

使用枚舉

和struct的成員訪問符號 . 不同的是,枚舉類型要想訪問其成員,幾乎無一例外的要用到模式匹配。并且, 你可以寫一個 Direction::West,但是你現(xiàn)在還不能寫成 Direction.West, 除非你顯式的 use 它 。雖然編譯器足夠聰明能發(fā)現(xiàn)你這個粗心的毛病。

關于模式匹配,我不會說太多,還是舉個栗子

enum SpecialPoint {
    Point(i32, i32),
    Special(String),
}

fn main() {
    let sp = SpecialPoint::Point(0, 0);
    match sp {
        SpecialPoint::Point(x, y) => {
            println!("I'am SpecialPoint(x={}, y={})", x, y);
        }
        SpecialPoint::Special(why) => {
            println!("I'am Special because I am {}", why);
        }
    }
}

吶吶吶,這就是模式匹配取值啦。 當然了, enum 其實也是可以 impl 的,一般人我不告訴他!

對于帶有命名字段的枚舉,模式匹配時可指定字段名

match sp {
    SpecialPoint::Point { x: x, y: y } => {
        // ...
    },
    SpecialPoint::Special(why) => {}
}

對于帶有字段名的枚舉類型,其模式匹配語法與匹配 struct 時一致。如

struct Point {
    x: i32,
    y: i32,
}

let point = Point { x: 1, y: 2 };

let Point { x: x, y: y } = point;
// 或
let Point { x, y } = point;
// 或
let Point { x: x, .. } = point;

模式匹配的語法與 if letlet 是一致的,所以在后面的內(nèi)容中看到的也支持同樣的語法。

上一篇:消息傳遞下一篇:特性