iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 26
0
自我挑戰組

WebAssembly + Rust 的前端應用系列 第 26

[Day 26] Rust Packages and Crates (實作 Actix 後的說明以及補充 PART1)

各位好,今天要把前幾天倉促完成之下的專案有些不太懂的觀念整理出來並且再重新介紹一下,那麼以下是整理出來我不熟悉的主題。

  • Struct
  • Managing Growing Projects with Packages, Crates, and Modules
  • Storing Lists of Values with Vectors
  • Advanced Types
  • Macros

這些主題的篇幅其實從官網看起來是都不算少,基本上我會盡量在鐵人賽完成這些。

那麼就廢話不多說我們從第一個主題 Struct 開始吧!

Struct

Struct 顧名思義呢就是結構的意思,因此他的目的就是幫助開發者可以定義一個資料的結構,其實有點類似之前介紹過的 tuples,不過 Struct 更加靈活因為不受順序的限制影響而 tuples 一定要依照順序才可以。

這邊舉個 User 的例子,

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

其實筆者認為蠻像資料庫 Schema 的我想應該是同的概念。

剛剛也有提到 Struct 本身不受順序的限制影響,舉例來說我們可以用 Struct 新增一個 user,

let user1 = User {
    email: String::from("someone@example.com"),
    username: String::from("someusername123"),
    active: true,
    sign_in_count: 1,
};

println!("{}", user1.email); // someone@example.com

如果有寫過 JS 的同學可能也會覺得看起來蠻像 obj 的因為就連讀資料的方式都一樣,而如果是 mutable 的也可以重新 assign 裡面的其中之一的欄位,例如,

let mut user1 = User {
    email: String::from("someone@example.com"),
    username: String::from("someusername123"),
    active: true,
    sign_in_count: 1,
};

user1.email = String::from("anotheremail@example.com");

不過要注意的是是在宣告變數的時候裡面不能有缺少的欄位,例如這樣是不行的,

let mut user2 = User {
    email: String::from("someone@example.com"),
    username: String::from("someusername123"),
    active: true
    // 這邊少了 sign_in_count
  };

還有就是無法只有單一個欄位是 mutable 的如果要 mutable 就是必須整個物件都是。

Struct 變數欄位相同名稱縮寫

然後如果要 assign 變數的名稱和欄位名稱是相同的也可以用縮寫的方式,

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

這個語法是不是跟 js 很像呢?他原本應該是長這樣,

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

Struct Update Syntax

這個如果懂 js 的同樣肯定不莫生直接上例子,

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

println!("{}", user2.email); // another@example.com

若是沒學過 js 的簡單來說就是把 user1 的資料在 user2 沿用但是若是有新的值例如上面例子的 emailusername 這兩個欄位就會取代掉 user1 的欄位。

Note: 學過 JS 的同學要注意一下 rust 他的 spread 並沒有順序的差別,因此這樣寫是錯的,

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

Tuple Structs

你也可以定義長得很像 tuple 的 structs 他就稱作 tuple structs ,直接舉例,

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

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

問題是什麼時候會用到呢?其實也就是當我們需要替 tuple 命名一種類型的時候可以使用。原本的 tuple 宣告方式是長這樣,順便複習一下。

  let tup: (i32, f64, u8) = (500, 6.4, 1);
  let (x, y, z) = tup;
  println!("The value of y is: {}", y);
  println!("The value of tup.0 is: {}", tup.0);
  println!("The value of tup.1 is: {}", tup.1);
  println!("The value of tup.2 is: {}", tup.2);

因此當需要一種 type 來定義 tup 時 tuple structs 就派上用場了,而且 tuple structs 也包含我們剛剛提到的所有特色在使用上方便許多。

Unit-Like Structs Without Any Fields

最後就是我們也可以定義完全沒有任何欄位的 Struct 就稱作 unit struct,官網的意思是說當你某些時候要定義一種類型但是沒有任何欄位時就會派上用場,要在後面的章節才會有比較深入的介紹。

Ownership of Struct Data

最後提到 lifetime 的問題,而這也是我在之前寫 Actix 專題時有碰到的,我們先引一段官方的說明。

Ownership of Struct Data
In the User struct definition in Listing 5-1, we used the owned String type rather than the &str string slice type. This is a deliberate choice because we want instances of this struct to own all of its data and for that data to be valid for as long as the entire struct is valid.

It’s possible for structs to store references to data owned by something else, but to do so requires the use of lifetimes, a Rust feature that we’ll discuss in Chapter 10. Lifetimes ensure that the data referenced by a struct is valid for as long as the struct is. Let’s say you try to store a reference in a struct without specifying lifetimes, like this, which won’t work:

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

fn main() {
    let user1 = User {
        email: "someone@example.com",
        username: "someusername123",
        active: true,
        sign_in_count: 1,
    };
}

The compiler will complain that it needs lifetime specifiers:

error[E0106]: missing lifetime specifier
 -->
  |
2 |     username: &str,
  |               ^ expected lifetime parameter

error[E0106]: missing lifetime specifier
 -->
  |
3 |     email: &str,
  |            ^ expected lifetime parameter

In Chapter 10, we’ll discuss how to fix these errors so you can store references in structs, but for now, we’ll fix errors like these using owned types like String instead of references like &str.

這邊其實並沒有講得很詳細看起來要到後面的章節才會介紹,因此我想大致用我理解的方式說明一下。

第一段是在說為什麼不用 &str 的方式寫這是故意的,因為要讓 Struct 的欄位都是有效的。

第二段是在說 ownership 而 Struct 也有可能把其中的欄位的 references 借給別人而要做到這樣的話就需要用到 lifetime 而這在後面的章節才會多做介紹。

總結

今天算是把之前不太懂的 Struct 給完整的搞懂,另外就是有稍微摸到 lifetime 的問題對他比較有譜了,若是下次再遇到可再在查文件。

那麼以上就是今天的內容我們明天見!

最後一樣有問題歡迎發問

/images/emoticon/emoticon07.gif

參考網址

ch05-01-defining-structs


上一篇
[Day 25] Rust Actix PART6
下一篇
[Day 27] Rust Packages and Crates (實作 Actix 後的說明以及補充 PART2)
系列文
WebAssembly + Rust 的前端應用30

尚未有邦友留言

立即登入留言