原始型別 (Primitive Types)
Move 是靜態型別語言:每個值都有一個在編譯時期已知的型別。本節介紹其中最簡單的型別——內建的原始型別:布林值與無號整數。再加上下一節介紹的地址,它們構成了所有其他型別的基礎。
本章的程式碼範例皆為摘錄:如下方所示的運算式位於模組中的函式內——通常是測試函式——為求簡潔,我們省略了模組。若要自行嘗試範例,請將其放入 Hello World 章節所建立套件的 #[test] 函式中,並執行 sui move test。
變數與指派 (Variables and Assignment)
變數使用 let 關鍵字宣告,且預設為 不可變:一旦指派值後, 便無法取代。需要變更的變數會以 let mut 宣告,且只有在此情況下, 才能使用 = 運算子重新指派:
// The type annotation is optional when it can be inferred.
let x: bool = true;
let y = 10u8;
// A `mut` variable can be reassigned with the `=` operator.
let mut z: u8 = 42;
z = 43;
型別註記——名稱後方的 : u8——在編譯器能從值或後續使用方式推斷 型別的任何地方皆為選用;將其明確寫出是為了清楚,而非必要。
也可以再次宣告變數來重複使用變數名稱,這稱為 遮蔽。不同於 重新指派,遮蔽會建立新的變數,因此可用於不可變變數,且能變更 型別:
let x: u8 = 42;
// The new `x` replaces the previous one, and may
// even have a different type.
let x: u16 = (x as u16) + 1;
布林值 (Booleans)
bool 型別恰有兩個值——關鍵字 true 與 false——而編譯器一律會推斷它,因此 bool 永遠不需要型別註記。布林值可與邏輯運算子 &&(且)、||(或)和 !(非)搭配使用,其中 && 與 || 會短路:如果左側已決定結果,就不會評估右側。
// The type of a boolean is always inferred.
let is_ready = true;
let is_done = false;
// Logical operators: `&&` (and), `||` (or), and `!` (not).
let in_progress = is_ready && !is_done;
布林值儲存旗標並驅動條件判斷——即 控制流程 章節涵蓋的 if 與 while 運算式。
整數型別 (Integer Types)
Move 有六種整數型別,僅大小不同,而且全都是 無號:Move 中沒有負整數,也沒有專用的帶號型別。
| 型別 | 大小(位元) | 最大值 |
|---|---|---|
| u8 | 8 | 255 |
| u16 | 16 | 65_535 |
| u32 | 32 | 4_294_967_295 |
| u64 | 64 | 18_446_744_073_709_551_615 |
| u128 | 128 | 2128 − 1 |
| u256 | 256 | 2256 − 1 |
主要使用的是 u64:代幣數量、大小與索引都使用它。整數常值可用十進位(42)撰寫,並可選擇加入底線以提升可讀性(1_000_000),或使用帶有 0x 前綴的十六進位(0x2A):
let small: u8 = 42;
let medium: u16 = 1_000; // underscores improve readability
let large: u256 = 100_000_000_000;
let hex: u64 = 0x2A; // hexadecimal literal, 42
雖然 true 與 false 明確是布林值,但像 42 這樣的常值可能是六種整數型別中的任何一種。編譯器會根據值的使用方式推斷型別,預設為 u64;當推斷不足,或明確指定可讓程式碼更易讀時,可以透過型別註記或常值後綴指定型別:
// Both are equivalent.
let x: u8 = 42;
let x = 42u8;
運算 (Operations)
Move 支援整數的標準算術運算:加法、減法、乘法、除法與模數(餘數)。它們都無法產生超出型別範圍的值;運算不會環繞,而是會中止:
| 語法 | 運算 | 中止條件 |
|---|---|---|
| + | 加法 | 結果對整數型別而言過大 |
| - | 減法 | 結果小於零 |
| * | 乘法 | 結果對整數型別而言過大 |
| % | 模數(餘數) | 除數為 0 |
| / | 截斷除法 | 除數為 0 |
除法為 截斷式:不存在小數值,且任何餘數都會被捨棄,因此 7 / 2 為 3。整數也可以使用 ==、!=、<、>、<= 與 >= 進行比較,產生 bool:
let a = 10u8;
let b = 20u8;
// Comparison produces a `bool`; the operands must be of the same type.
let is_less = a < b; // true
let is_equal = a == b; // false
在每項運算與比較中,運算元的型別 必須相符:整數型別之間沒有隱含轉換,將 u8 加上 u64 會造成編譯錯誤。若要對不同型別進行運算,必須明確轉型其中一個運算元。
如需更多運算,包括位元運算,請參閱 Move 參考文件。
使用 as 轉型 (Casting with as)
as 運算子可將整數從一種型別轉換為另一種型別。請注意,包含轉型的運算式通常需要以括號包住,以避免歧義:
let x: u8 = 42;
let y: u16 = x as u16;
let z = 2 * (x as u16); // ambiguity requires parentheses
向 上 轉型至較大的型別時一定成功。向 下 轉型必須能容納該值:不同於會悄悄截斷值的語言,Move 在值超出範圍時會中止:
let x: u16 = 300;
let y = x as u8; // ABORTS! 300 does not fit into `u8`
向上轉型的常見用途是為無法容納於原始型別的中間結果預留空間:
// The same values that would overflow `u8` arithmetic
// fit comfortably once upcast to `u16`.
let x: u8 = 255;
let y: u8 = 255;
let z: u16 = (x as u16) + ((y as u16) * 2);
溢位與下溢 (Overflow and Underflow)
如運算表所示,Move 中的算術運算絕不會環繞。結果無法容納於型別中的運算——過大或小於零——會在執行階段中止:
let x = 255u8;
let y = 1u8;
let z = x + y; // ABORTS! The result does not fit into `u8`
這是一項刻意設計的安全功能。靜默溢位是智慧合約錯誤的典型來源——餘額環繞為零,或因為某個值悄悄變小而使檢查通過。Move 會將每一種這類情況轉為明確失敗,並還原交易。