<<Rust程序設計語言>>個人版(3.1: 變量與可變性/3.2: 數據類型)

常見程序設計概念

變量與可變性

rust默認你創建的變量是不可變變量, 這是爲了提高代碼的安全性, rust鼓勵你多使用不可變變量, 當然當你有需要的時候, 你可以將其變成可變變量

我們來創建一個新的項目來開始本章 cargo new variables

修改main.rs

fn main() {
    let x = 5;
    println!("{}", x);
    x = 6;
    println!("{}", x);
}

運行他, 會報錯

➜  variables git:(master) ✗ cargo run
   Compiling variables v0.1.0 (/Users/Work/Code/Rust/student/variables)
error[E0384]: cannot assign twice to immutable variable `x`
 --> src/main.rs:4:5
  |
2 |     let x = 5;
  |         -
  |         |
  |         first assignment to `x`
  |         help: make this binding mutable: `mut x`
3 |     println!("{}", x);
4 |     x = 6;
  |     ^^^^^ cannot assign twice to immutable variable

error: aborting due to previous error

For more information about this error, try `rustc --explain E0384`.
error: could not compile `variables`.

To learn more, run the command again with --verbose.

這裏的錯誤是 cannot assign twice to immutable variable, 告訴你不能對不可變變量二次賦值

rust加入這個限制是爲了代碼的安全性, 因爲對於開發者來說, 建立不可變變量的目的是不希望他能夠修改, 如果你可以修改, 就可能會導致代碼朝着無法預料的方向發展

在rust中, 編譯器會確保你創建的不可變變量永遠不被修改, 這能讓你更加專注的考慮正常的邏輯

同時我們可以設置可變變量來解決問題, 使用 mut 來標識這個變量是可變的, 比如

fn main() {
    let mut x = 5;
    println!("{}", x);
    x = 6;
    println!("{}", x);
}

此時就可以正常的運行

➜  variables git:(master) ✗ cargo run
   Compiling variables v0.1.0 (/Users/Work/Code/Rust/student/variables)
    Finished dev [unoptimized + debuginfo] target(s) in 0.60s
     Running `target/debug/variables`
5
6

對於大型的項目來說, 將變量設置成可變然後更改其值要比將變量設置爲不可變然後讀取其值複製給新的變量效率更高

變量與常量的區別

當你創建一個不可變變量 時, 你仍然可以在後續中修改他爲可變變量, 但是對於常量來講, 創建過後及 無法通過任何方式修改, 他不是預設無法修改, 而是永遠無法修改

當你使用 const 關鍵字來創建常量時, 必須要指定他的類型

常量只能被常量表達式來設置, 不能通過某個函數的返回值或者運行時產生的數值來設置

rust的常量通常使用全大寫的字母, 多個單詞間使用_連接的方式來命名, 例如

const MAX_POINTS: u32 = 100_000;  // 創建常量 MAX_POINTS , 類型是u32, 值是100000

其中, 100000100_000是一樣的數值, 加_可以提升可讀性

遮蔽(shadowing)

對於變量來講, 我們可以在給某個變量賦值後, 通過遮蔽的方式來重新賦值, 或者說是覆蓋

使用 let 來對一個已經存在的變量重新聲明, 這樣新的變量就會遮蔽原來的變量

fn main() {
    let a = 1;
    println!("{}", a);
    let a = 5;
    println!("{}", a);
    let a = a+1;
    println!("{}", a);
}

輸出

1
5
6

需要注意的是, 我們在遮蔽時, 必須要帶上let關鍵字, 否則會出現編譯錯誤

我們知道, 使用 mut 可以設置可變變量, 他與遮蔽的不同是遮蔽對某個變量操作時可以設置不同的類型, 而mut不可以, 比如

    let spaces = "   ";
    let spaces = spaces.len();

這裏 spaces 從字符串類型遮蔽成了數字類型

    let mut spaces = "   ";
    spaces = spaces.len();

這段代碼是無法編譯的, 因爲mut無法改變值的類型

fn main() {
    let mut a = 1;
    println!("{}", a);
    a = "dadad"
}
➜  variables git:(master) ✗ cargo run
   Compiling variables v0.1.0 (/Users/Work/Code/Rust/student/variables)
error[E0308]: mismatched types
 --> src/main.rs:4:9
  |
4 |     a = "dadad"
  |         ^^^^^^^ expected integer, found `&str`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
error: could not compile `variables`.

數據類型

數據類型分爲兩種: 純量(scalar)複合(compound)

Rust是一個靜態類型語言, 因此, 所有變量在編譯時編譯器就必須知道他的類型, 如果無法得知就會導致編譯錯誤

當然, 一般情況下, 編譯器會自動識別出我們想要設置的類型, 比如

let a = 1;

但是對於函數來講, 有時候可能有多個可能的結果返回, 比如

let guess: u32 = "42".parse().expect("error!");

在編譯時就會出現錯誤

   Compiling variables v0.1.0 (/Users/Work/Code/Rust/student/variables)
warning: unused variable: `guess`
 --> src/main.rs:2:9
  |
2 |     let guess: u32 = "42".parse().expect("這不是數字!");
  |         ^^^^^ help: consider prefixing with an underscore: `_guess`
  |
  = note: `#[warn(unused_variables)]` on by default

    Finished dev [unoptimized + debuginfo] target(s) in 0.40s
     Running `target/debug/variables`

純量

純量的類型代表單一的值, rust有四種純量, 分別爲 整數/浮點數/字符/布爾

整數

整數指的是沒有小數點的數字, 之前我們使用過 u32, 還有其他的幾種

長度 帶正負號 不帶正負號(正整數)
8位 i8 u8
16位 i16 u16
32位 i32 u32
64位 i64 u64
128位 u128
系統架構 isize usize

首先, 帶正負號與不帶正負號的區別就是字面意思, 帶正負號可以表示負數, 不帶則只能表示正整數

然後, 長度代表了這個變量在記憶體內存儲的大小, 也影響了數字的範圍, 而isizeusize則與系統有關, 如果你的系統是32位, 則是32位的長度, 64就是64

對於某個數字, 你可以將類型拼在值後面來標識這個值是什麼類型, 比如54u16代表是u16位的值是54

默認的整數是i32, 這在rust中是運行速度最快的類型

整數溢出

舉個例子, 如果你創建了一個u8的變量, 那麼他的長度只有8位, 只能容納 1-255 之間的數值, 如果你給他賦值成 256, 或者大於255的數字, 就會發生整數溢出, 如果你是在默認情況下編譯的話, 則會在運行時造成panic錯誤, 如果你是編譯成穩定版(release), 程序在檢測到溢出時會處理這個錯誤, 並不會讓他直接panic但是會使原有的值發生變化, 造成邏輯錯誤.

浮點數

浮點數有 f32(單精度)f64(雙精度), 默認是f64

fn main() {
    let x = 2.0; // f64

    let y: f32 = 3.0; // f32
}

數值運算

數值之間當然可以進行運算

fn main() {
    // 加
    let sum = 5 + 10;

    // 減
    let difference = 95.5 - 4.3;

    // 乘
    let product = 4 * 30;

    // 除
    let quotient = 56.7 / 32.2;

    // 求餘
    let remainder = 43 % 5;
}

布爾值

true(真)false(假)

fn main() {
    let t = true;

    let f: bool = false; 
}

字符

單個字符使用單引號來表示, 例如一個字母/中文/表情, 當我們需要表達不止一個字符時, 就需要使用雙引號

fn main() {
    let c = 'z';
    let z = 'ℤ';
    let heart_eyed_cat = '😻';
    let a = "1111111";
    let b = '我';
    let d: char = '他';
}

rust的charunicode類似, 確又不是完全一樣, 我們之後會詳細解釋

複合類型

複合類型有兩個最基本的, 元組(tuples)陣列(arrays)

元組

元組是將不同或相同的數據類型放置到一起的常見用法, 需要注意的是元組的長度在確定後無法更改

元組的設立是用括號括起來, 因爲一個元組內的每個數據都有可能有獨立的數據類型, 所以在必要時也可以指定每個的類型, 如果不指定rust會自己判斷

fn main() {
    let tup: (i32, f64, u8) = (500, 6.4, 1);
}

這裏的變量tup就是元組, 元組內有多個元素, 我們可以使用模式配對(pattern matching), 來獲取每一個元素的值

fn main() {
    let tup = (500, 6.4, 1);

    let (x, y, z) = tup;

    println!("y 的數值為:{}", y);
}

這種將元組打散然後分別賦值給 x/y/z, 需要保證接受者和元組的長度是一致的, 這種也被成爲解構(destructuring)

還有一種通過索引來取其中某個值的方法, 在rust中通過.來獲取, 比如

fn main() {
    let x: (i32, f64, u8) = (500, 6.4, 1);

    let five_hundred = x.0;

    let six_point_four = x.1;

    let one = x.2;
}

索引和其他語言一樣, 都是從0開始的, 並且不能超出索引大小

陣列

與元組不同的是, 一個陣列的元素必須是同一個數據類型, 而且和元組一樣, 陣列長度在確定後無法更改

陣列的設立是使用中括號, 例如

fn main() {
    let a = [1, 2, 3, 4, 5];
}

當你希望將數據放置在堆棧上而不是堆上時(我們之後會詳細解讀), 或者當你想要確保始終擁有固定數量的元素時, 你可以使用陣列來做

當你不知道有多少個數據時(很多時候是這樣), 陣列和元組都將不適合使用, 此時, rust標準庫提供了矢量(vector)類型, 他是可以改變長度的, 如果你不確定應該使用矢量還是陣列時, 你應該使用矢量來創建, 矢量的具體細節之後再講述

當你明確的知道數據的數量時, 你就可以放心的使用陣列, 例如, 每年有12個月

let months = ["一月", "二月", "三月", "四月", "五月", "六月", "七月",
              "八月", "九月", "十月", "十一月", "十二月"];

這裏rust會自己判別陣列的類型和長度, 當然你也可以指定

let a: [i32; 5] = [1, 2, 3, 4, 5];

這裏, i32 是每個元素的類型, 5是這個陣列的長度

如果你想給每個元素都設置一個默認值, 可以這樣寫

let a = [3; 5];

會生成陣列[3, 3, 3, 3, 3], 即a的長度爲5, 每個默認值都是3

獲取陣列元素

我們可以使用索引來獲取對應的值

fn main() {
    let a = [1, 2, 3, 4, 5];

    let first = a[0];
    let second = a[1];
}

無效的陣列元素獲取

對於陣列來講, 當你獲取不存在的索引是, 有可能會出現編譯通過而運行時出現錯誤的情況, 比如

fn main() {
    let a = [1, 2, 3, 4, 5];

    let element = a[10];

    println!("元素的數值為:{}", element);
}

我們使用check功能發現並沒有檢查出錯誤

➜  variables git:(master) ✗ cargo check
    Checking variables v0.1.0 (/Users/Work/Code/Rust/student/variables)
    Finished dev [unoptimized + debuginfo] target(s) in 0.12s

但是當我們運行時, 當執行到這裏時, rust會拋出錯誤, 而對於其他的底層語言(例如C), 則不會報錯但是會得到不可預料的值, rust這樣做減少了不可預料的BUG的產生, 這是Rust的安全原則之一

➜  variables git:(master) ✗ cargo run
   Compiling variables v0.1.0 (/Users/Work/Code/Rust/student/variables)
error: index out of bounds: the len is 5 but the index is 10
 --> src/main.rs:4:19
  |
4 |     let element = a[10];
  |                   ^^^^^
  |
  = note: `#[deny(const_err)]` on by default

error: aborting due to previous error

error: could not compile `variables`.

To learn more, run the command again with --verbose.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章