Rust中常量為什麼用let不用const,變數用let mut不用var?

今天第一次看,暈,看了網上說的,感覺解釋有點牽強,let mut,let,,很新奇嗎,還有另有隱情,求解答?


let和mut是很多函數式語言的傳統關鍵字,Rust用這倆感覺挺自然。

多接觸些用不同關鍵字的語言,再回頭看的時候就會淡定許多。沒啥隱情不隱情的。

常量聲明,val、const、let、def都是很常見的關鍵字。變數聲明,var、def、mut、mutable之類的關鍵字都可能;有些語言乾脆把這個關鍵字也省了,用特殊的賦值符號來表示這是一個常量聲明(例如Go的 := ,或者C系的聲明語法)。

具體用哪個詞就看設計者的taste…

例如說,Lisp系(像是Scheme和各種Lisp)、OCaml系(包括F#)、Haskell等等,「變數」聲明都是用let,所謂「let-binding」。

這裡「變數」要打引號是因為這裡聲明的實際上不是變數,而只是跟某個值綁定的一個名字,而這個名字在其作用域內不能改變綁定的值——也就是說其實是常量而不是變數。

Rust的let聲明的綁定是不可變的,所以下面的代碼是錯誤:

let x = 5;
x = 10; // error: re-assignment of immutable variable `x`

但是同一個名字可以用新的let聲明綁定到新的值。這樣得到的是一個新的「變數」,並不是對原有變數的修改——原有的綁定仍然是不可變的。

所以下面這樣是可以的:

let x = 5; // warning: unused variable: `x`, #[warn(unused_variables)] on by default
let x = 10;
println!("x = {}", x);

留意編譯器會給第一個x聲明一個警告,也就是說它清楚的將前一個x聲明與後一個x聲明認知為兩個不同的變數;換句話說,前一個x聲明的作用域到後一個x聲明的等號右手邊開始就被後者的作用域所遮蔽(shadow),所以後面新出現對x變數的引用時引用的是後者,但此時兩者仍然同時存在。

不要被「名字相同」騙了喔。

而跟OCaml同為ML系語言的Standard ML(SML),它的let-binding的語法則是用在let里用val關鍵字,用val的地方比用let多。

Scala聲明常量就用val,聲明變數用var。

然後mutable。Rust用let mut,就像F#的let mutable的簡寫形式一樣;

而像OCaml的話聲明可變欄位直接用mutable關鍵字,聲明可變變數則是用ref關鍵字;

而Scheme用define聲明的變數則可以後面再用set!修改。


記住 let 是函數式語言中的綁定(binding),而不是賦值(assignment)

let 這麼強大的東西為什麼不用呢,const,var 能玩匹配嗎

假設一個函數或方法返回一個 tuple,需要把它解析出來

fn f() -&> (str, i32) {}
let (a, b) = f()
let (mut a, b) = f()
let (_, b) = f()
let (ref a, b) = f()

如果返回的是一個結構體,可以這麼玩

struct Point {
x: f64,
y: f64,
}
fn f() -&> Point {}
let Point{x: a, y:b} = f();
let Point{x: mut c, y:_} = f();
let Point{x: _, y: ref d} = f();

如果用 const, var 應該怎麼寫上面這些呢

const a;
var b;
Point p = f(); //Tuple t = f();
a = p.x; // a = p.0;
b = p.y; // b = p.1;

另外還有 if let 和 while let 這樣的東西,玩的也是模式匹配,直接把 Some 裡面非空的東西摳出來使用,Simple, Clear, Powerful!

if let Some(x) = option {
foo(x);
} else {
bar();
}

while let Some(x) = option {
println!("{}", x);
}

另外 const 有必要麼,寫過 C++ 或 Java 大型項目應該體會到到處是 const/finally,之前有人統計過 Rust 項目里 let mut 與單 mut 的比例(即mut和const的比例),其中單 let 是 let mut 的3倍,與其到處寫 const 防止被修改,為什麼不默認就是不可變 呢,這樣可以省去許多擊鍵數

關於這個問題早先郵件列表裡也有過很長很長的討論,有興趣的可以去看看

Update:Rust 也有 const,不過與其它語言中的不太一樣,更像 C 中 define 定義的常量,因為會直接 inline 到使用的地方。


C、C++里的const是假的,表達的根本就不是常量的意思,而是readonly的意思。


let代表綁定。從語義上來看 let,有「允許」的意思:

let x = 1;

允許x作為1的不可變綁定(默認行為)。

let mut x = 1;

允許x作為1的可變綁定。

為什麼不用var? 因為要保證語言的一致性。

const有啥語義? 常量。

你可以再看看ES6里let和const的區別,let是綁定了塊級作用域,而不能變數提升,const也一樣是遵循其語義,定義常量。

設計一門語言,要考慮關鍵字的命名所帶來的語義,包含了什麼樣的啟示,還要考慮到語言的一致性。


rust的let聲明的是變數,不是常量。下面的代碼是合法的:

fn main() {
let a = 5;
let a = 6;
}

a並不是一個常量,這個名字可以綁定到另一個值。a是個不可變的變數,可以指向另一個值,但不可以改變該值。而rust中的常量是不允許這樣的。

所以let和let mut都是聲明一個變數,所以不會用const。

rust是有常量聲明的,而且用的就是const:

const FOO: i32 = 42;

常量重複聲明,編譯器會報錯(對比上面的let聲明):

const a: i64 = 5;
const a: i64 = 6;

let可以用pattern matching來解構數據:

struct Foo{
bar: i64,
baz: i64,
}

fn main() {
let s = Foo{bar: 1, baz: 2};
let Foo{bar: a, baz: b} = s;
println!("{} {}", a, b); // 1 2
}

這和很多函數式語言是一樣的,所以沿用let這個命名是自然的。


郵件列表裡講過, 主要是因為 mut 不能用 var 代替, mut 經常用來標記參數, 既然免不掉 mut, 用 let mut 作 var.


let 是 binding 不是變數或常量


推薦閱讀:

c++現在有哪些GUI庫可以用?
計算機系學生的你,有哪些課程覺得自己沒有學明白?
為什麼函數能遞歸調用自己?
如何成長為一個優秀的C++程序員?
有哪些和編程有關的經典語句?

TAG:編程語言 | 編程 | Rust編程語言 |