使用 let 來宣告變量,且是可變的,可以直接更新。
let x = 1;
let y = x + x;
let z
z = true // 也可以稍後在分配值
let x;
x + x // ERROR!
let x;
if (cond) x = 0;
x + x // ERROR!
可以包含下劃線,大小寫英文字母,數字 0 to 9
必須以下劃線或字母開頭 (不能大寫字母)
// all valid
let x = e;
let _x = e;
let _A = e;
let x0 = e;
let xA = e;
let foobar_123 = e;
// all invalid
let X = e; // ERROR!
let Foo = e; // ERROR!
Move 擁有自動推斷型別能力,但也允許顯示類型註釋,提高易讀性。
而當無法自動推斷類型時,則需要自行添加類型
let x: T = e; // 變數 x 的型別是 T
let _v1 = Vector::empty(); // ERROR!
// ^^^^^^^^^^^^^^^ Could not infer this type. Try adding an annotation
let v2: vector<u64> = Vector::empty(); // no error
let 可以使用元組
let () = ();
let (x0, x1) = (0, 1);
let (y0, y1, y2) = (0, 1, 2);
let (z0, z1, z2, z3) = (0, 1, 2, 3);
// 數量必需一致
let (x, y) = (0, 1, 2); // ERROR!
let (x, y, z, q) = (0, 1, 2); // ERROR!
let 也可以搭配 struct,用法如下
struct 之後會介紹給大家
let T { f1: u64, f2: u64 } = T { f1: 1, f2: 2 };
在上面的結構示範中,let 中的綁定值被移動,破壞了結構值並綁定了他的 field
如果希望不移動和破壞結構值,可以使用 &
& 代表不可以修改
let T { f1: local1, f2: local2 } = T { f1: 1, f2: 2 };
// local1: u64
// local2: u64
let t = T { f1: 1, f2: 2 };
let T { f1: local1, f2: local2 } = &t;
// local1: &u64
// local2: &u64
以開頭 _ 的變量,將被忽略,不會引入新變量
fun three(): (u64, u64, u64) {
(0, 1, 2)
}
let (x1, _, z1) = three(); // x1 = 0, z1 = 2
let (x2, _y, z2) = three(); // x1 = 0, z1 = 2
我們來詳細看一下 let 結構
一般宣告時,整個表達式就是 let-binding 包含了左側的 local variable, 和右側的 initializer 值,type annotation 則會左側的 :
符號之後
結構宣告時,相比一般宣告,多了幾個部分
左測結構中,每個 field 會進行所謂 field-binding,將右側的 initializer 綁定過去
let (x, y): (u64, u64) = (0, 1);
// ^ local-variable
// ^ pattern
// ^ local-variable
// ^ pattern
// ^ pattern-list
// ^^^^ pattern-list
// ^^^^^^ pattern-or-list
// ^^^^^^^^^^^^ type-annotation
// ^^^^^^^^ initializer
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ let-binding
let Foo { f, g: x } = Foo { f: 0, g: 1 };
// ^^^ struct-type
// ^ field
// ^ field-binding
// ^ field
// ^ local-variable
// ^ pattern
// ^^^^ field-binding
// ^^^^^^^ field-binding-list
// ^^^^^^^^^^^^^^^ pattern
// ^^^^^^^^^^^^^^^ pattern-or-list
// ^^^^^^^^^^^^^^^^^^^^ initializer
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ let-binding
除了通過賦值直接修改變量外,還可以通過可變引用來修改局部&mut
。
&mut 代表可以修改
let x = 0;
let r = &mut x;
*r = 1;
Debug::print<u8>(&x); // x is 1
}
在以下情況特別有用
根據某些條件修改本地值
let x = 0;
let y = 1;
let r = if (cond) &mut x else &mut y;
*r = *r + 1;
Debug::print<u8>(&x); // x is 1
透過另一個函數來修改變量
let x = 0;
modify_ref(&mut x);
每組 { }
就是一個範圍,範圍的存取只能向內嵌套作用,不能向外。
{
let x = 0;
{
let y = x + 1; // valid
}
}
let x = 0;
{
let y = 1;
};
x + y // ERROR!
// ^ unbound local 'y'
script {
fun main() {
let _ = 1;
}
}
如果 let 使用了一個已經在範圍內的變量名字,則該範圍的其餘部分將無法在訪問先前的變量,稱為陰影。且不用遵照前者的類型
要注意得是,陰影發生後,儲存在本地的值依然存在,但將不在可訪問。
let x = 0;
let x = true; // x is shadowed, the value is true, nothing error
是由分號分隔的一系列語句,表達式塊的結果值是區塊內最後一個表達式的值
{ let x = 1; let y = 1; x + y } // 塊的結果是 x + y
{ let x; let y = 1; x = 1; x + y } // 可以是 let 聲明或是使用表達式
函數調用是 type 的另一種常見表達方式
{ let v = Vector::empty(); Vector::push_back(&mut v, 1); v }
在 Move 中,所有變量都可以使用兩種方式
若未指定,Move 會自動推斷使用
// 使用複製
let x = 0;
let y = copy x + 1;
let z = copy x + 2;
// 使用移動
let x = 1;
let y = move x + 1;
// ------ Local x was moved here
let z = move x + 2; // Error!
// ^^^^^^ Invalid usage of local 'x'
y + z
Move 的類型系統會阻止一個值在移動後被使用,與 let 聲明描述檢查相同,可防止變量在賦值之前被使用
代表返回值的代碼,在 Move 裡,必須在每個表達式後加上 ;
,否則會無法編譯
script {
fun empty() {
() // this is an empty expression
}
}
可以使用 let
宣告變量來儲存表達式的值
script {
fun main() {
let a; // 如果拿掉 ; 將會錯誤
let b = true;
let c = 10;
let d = 0x1;
}
}
本篇從本地變量開始,介紹類型註釋和多種聲明,以及 Scope 的範圍,接著進入表達式和如何使用。而有些東西像是 &
, &mut
和自動推斷因為篇幅關係,我們在後面會專門做介紹。讓我們 Move to Day5
相關資料
Tuple: https://en.wikipedia.org/wiki/Tuple
Shadowing: https://en.wikipedia.org/wiki/Variable_shadowing