Rust 基礎學習


所有權:

  變量具有唯一所有權。如果一個類型擁有 Copy trait,一個舊的變量在將其賦值給其他變量后仍然可用。除此之外,賦值意味着轉移所有權。Rust 不允許自身或其任何部分實現了 Drop trait 的類型使用 Copy trait。

如下是一些 Copy 的類型:

  • 所有整數類型,比如 u32
  • 布爾類型,bool,它的值是 true 和 false
  • 所有浮點數類型,比如 f64
  • 字符類型,char
  • 元組,當且僅當其包含的類型也都是 Copy 的時候。比如,(i32, i32) 是 Copy 的,但 (i32, String) 就不是。

引用:

  引用指的是獲取對象或變量的內容而不獲取所有權。也意味着不能修改被引用的值。可以存在多個引用。

let s1 = Stri與使用 & 引用相反的操作是 解引用dereferencing),它使用解引用運算符,*ng::from("hello");

let len = calculate_length(&s1);   //引用

與使用 & 引用相反的操作是 解引用dereferencing),它使用解引用運算符 

可變引用:

  為了對引用的對象修改,可以在特定作用域中對特定數據僅存在唯一可變引用

let mut s = String::from("hello");

let r1 = &mut s;
let r2 = &mut s;       //Error

但,需要注意的是,不能在擁有不可變引用的同時擁有可變引用。即,要么引用,要么可變引用。

let mut s = String::from("hello");

let r1 = &s; // no problem
let r2 = &s; // no problem
let r3 = &mut s; //  Error, BIG PROBLEM

Slice:

  數組是同一類型的對象的集合 T, 存儲在連續內存中。 用方括號 [] 創建數組, 以及它們的大小在編譯的時候判定,是它們的類型簽名的一部分 [T; size]

切片和數組相似,但它們的大小在編譯時是不知道的. 相反,切片是一個雙字對象,第一個字是一個指針中的數據,第二個字是切片的長度。切片可借用數組的截面,並具有式簽名 &[T]

 

結構體(struct):

struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

字段初始化簡寫語法field init shorthand):

fn build_user(email: String, username: String) -> User {
    User {
        email,
        username,
        active: true,
        sign_in_count: 1,
    }
}

 結構體更新語法struct update syntax)實現從其他結構體創建實例:

let user2 = User {
    email: String::from("another@example.com"),
    username: String::from("anotherusername567"),
    ..user1
};

使用結構體更新語法為一個 User 實例設置新的 email 和 username 值,不過其余值來自 user1 變量中實例的字段

 

元組結構體tuple structs

元組結構體有着結構體名稱提供的含義,但沒有具體的字段名,只有字段的類型。當你想給整個元組取一個名字,並使元組成為與其他元組不同的類型時,元組結構體是很有用的,這時像常規結構體那樣為每個字段命名就顯得多余和形式化了。

struct Color(i32, i32, i32);
struct Point(i32, i32, i32);

let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);

因為它們是不同的元組結構體的實例。你定義的每一個結構體有其自己的類型,即使結構體中的字段有着相同的類型。例如,一個獲取 Color 類型參數的函數不能接受 Point 作為參數,即便這兩個類型都由三個 i32 值組成。在其他方面,元組結構體實例類似於元組:可以將其解構為單獨的部分,也可以使用 . 后跟索引來訪問單獨的值,

類單元結構體unit-like structs

沒有任何字段,類似於 (),即 unit 類型。類單元結構體常常在你想要在某個類型上實現 trait 但不需要在類型中存儲數據的時候發揮作用。

結構體數據的所有權

可以使結構體存儲被其他對象擁有的數據的引用,不過這么做的話需要用上 生命周期lifetimes)。

struct 類型友好打印:

#[derive(Debug)] struct Rectangle {
    width: u32,
    height: u32,
}

fn main() {
    let rect1 = Rectangle { width: 30, height: 50 };

    println!("rect1 is {:#?}", rect1);
}

方法 與函數類似:它們使用 fn 關鍵字和名稱聲明,可以擁有參數和返回值,同時包含在某處調用該方法時會執行的代碼。不過方法與函數是不同的,因為它們在結構體的上下文中被定義(或者是枚舉或 trait 對象的上下文),並且它們第一個參數總是 self,它代表調用該方法的結構體實例。

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}
//方法
impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

fn main() {
    let rect1 = Rectangle { width: 30, height: 50 };

    println!(
        "The area of the rectangle is {} square pixels.",
        rect1.area()
    );
}

在 area 的簽名中,使用 &self 來替代 rectangle: &Rectangle,因為該方法位於 impl Rectangle上下文中所以 Rust 知道 self 的類型是 Rectangle。注意仍然需要在 self 前面加上 &,就像 &Rectangle 一樣。方法可以選擇獲取 self 的所有權,或者像我們這里一樣不可變地借用 self,或者可變地借用 self,就跟其他參數一樣。

這里選擇 &self 的理由跟在函數版本中使用 &Rectangle 是相同的:我們並不想獲取所有權,只希望能夠讀取結構體中的數據,而不是寫入。如果想要在方法中改變調用方法的實例,需要將第一個參數改為 &mut self通過僅僅使用 self 作為第一個參數來使方法獲取實例的所有權是很少見的;這種技術通常用在當方法將 self 轉換成別的實例的時候,這時我們想要防止調用者在轉換之后使用原始的實例

關聯函數

impl 塊的另一個有用的功能是:允許在 impl 塊中定義  以 self 作為參數的函數。這被稱為 關聯函數associated functions),因為它們與結構體相關聯。它們仍是函數而不是方法,因為它們並不作用於一個結構體的實例。你已經使用過 String::from 關聯函數了。

關聯函數經常被用作返回一個結構體新實例的構造函數。例如我們可以提供一個關聯函數,它接受一個維度參數並且同時作為寬和高,這樣可以更輕松的創建一個正方形 Rectangle 而不必指定兩次同樣的值:

impl Rectangle {
    fn square(size: u32) -> Rectangle {
        Rectangle { width: size, height: size }
    }
}

使用結構體名和 :: 語法來調用這個關聯函數:比如 let sq = Rectangle::square(3);。這個方法位於結構體的命名空間中::: 語法用於關聯函數和模塊創建的命名空間。

每個結構體都允許擁有多個 impl 塊。

枚舉:

舉例:

enum IpAddrKind {
    V4,
    V6,
}

可以像這樣創建 IpAddrKind 兩個不同成員的實例:

let four = IpAddrKind::V4;
let six = IpAddrKind::V6;

Reference :enum

下邊的IpAddr 枚舉的新定義表明了 V4 和 V6 成員都關聯了 String 值:

enum IpAddr {
    V4(String),
    V6(String),
}

let home = IpAddr::V4(String::from("127.0.0.1"));

let loopback = IpAddr::V6(String::from("::1"));

我們直接將數據附加到枚舉的每個成員上,這樣就不需要一個額外的結構體了。

用枚舉替代結構體還有另一個優勢:每個成員可以處理不同類型和數量的數據。IPv4 版本的 IP 地址總是含有四個值在 0 和 255 之間的數字部分。如果我們想要將 V4 地址存儲為四個 u8 值而 V6 地址仍然表現為一個 String,這就不能使用結構體了。枚舉則可以輕易處理的這個情況:

enum IpAddr {
    V4(u8, u8, u8, u8),
    V6(String),
}

let home = IpAddr::V4(127, 0, 0, 1);

let loopback = IpAddr::V6(String::from("::1"));

另一個示例:

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

這個枚舉有四個含有不同類型的成員:

  • Quit 沒有關聯任何數據。
  • Move 包含一個匿名結構體。
  • Write 包含單獨一個 String
  • ChangeColor 包含三個 i32

結構體和枚舉還有另一個相似點:就像可以使用 impl 來為結構體定義方法那樣,也可以在枚舉上定義方法。這是一個定義於我們 Message 枚舉上的叫做 call 的方法:

impl Message {
    fn call(&self) {
        // 在這里定義方法體
    }
}

let m = Message::Write(String::from("hello"));
m.call();

方法體使用了 self 來獲取調用方法的值。這個例子中,創建了一個值為 Message::Write(String::from("hello")) 的變量 m,而且這就是當 m.call() 運行時 call 方法中的 self 的值。

Option 

  Option是標准庫定義的另一個枚舉。Rust 並沒有空值,不過它確實擁有一個可以編碼存在或不存在概念的枚舉。這個枚舉是 Option<T>,而且它定義於標准庫中,如下:

enum Option<T> {
    Some(T),
    None,
}

Option<T> 枚舉是如此有用以至於它甚至被包含在了 prelude 之中,你不需要將其顯式引入作用域。另外,它的成員也是如此,可以不需要 Option:: 前綴來直接使用 Some 和 None。即便如此 Option<T>也仍是常規的枚舉,Some(T) 和 None 仍是 Option<T> 的成員。

<T> 語法是一個泛型類型參數。<T> 意味着 Option 枚舉的 Some 成員可以包含任意類型的數據。這里是一些包含數字類型和字符串類型 Option 值的例子:

let some_number = Some(5);
let some_string = Some("a string");

let absent_number: Option<i32> = None;

如果使用 None 而不是 Some,需要告訴 Rust Option<T> 是什么類型的,因為編譯器只通過 None 值無法推斷出 Some 成員保存的值的類型。

當有一個 Some 值時,我們就知道存在一個值,而這個值保存在 Some 中。當有個 None 值時,在某種意義上,它跟空值具有相同的意義:並沒有一個有效的值。

>Option<T>  比空值要好的原因是因為 Option<T> 和 T(這里 T 可以是任何類型)是不同的類型,編譯器不允許像一個肯定有效的值那樣使用 Option<T>。(也就是不能混用,要使用空類型,必須使用Option<T>,而非此類型的變量,永遠非空,不需要做空值檢查。)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM