Rust編程語言目前勢頭很猛,受到了幾大互聯網巨頭的青睞冒滩,比如谷歌浪谴、微軟因苹、Meta篇恒,亞馬遜等,還作為第二語言進入了Linux內核款筑,國內的華為和字節(jié)跳動等公司也有Rust相關的應用腾么。
本人是一個Rust的粉絲,寫過不少的Rust應用解虱,這里我總結一下我認為的Rust相比于其他編程語言的更為正確的設計:
Sum type
Sum type是比較學術的說法,常見于函數式語言于宙,但是sum type并非函數式語言的專屬悍汛,老大哥C語言的union
加上tag之后就是sum type(俗稱tagged union),而動態(tài)類型語言的變量的類型其實就是封裝出來的sum type類型谱俭,比如PHP的變量定義:
typedef union _zend_value {
zend_long lval; /* long value */
double dval; /* double value */
zend_refcounted *counted;
zend_string *str;
zend_array *arr;
zend_object *obj;
zend_resource *res;
zend_reference *ref;
zend_ast_ref *ast;
zval *zv;
void *ptr;
zend_class_entry *ce;
zend_function *func;
struct {
uint32_t w1;
uint32_t w2;
} ww;
} zend_value;
但是為啥某些主流的靜態(tài)類型語言卻缺失了這個概念(比如Java宵蛀、Go等)?我是沒想明白的糖埋,畢竟老大哥C語言都有這個概念了,缺失了sum type會導致某些需要動態(tài)的場景非常的別扭征候,比如序列化JSON祟敛,舉個例子,需要反序列化如下的JSON:
[1, "foo", null]
Java要怎么做跑揉?只能序列化到List<Object>
,然后靠類型推斷來判斷是Integer還是String還是Object(null)了历谍,這樣就喪失掉靜態(tài)類型的優(yōu)勢了。
而Rust的sum type關鍵字enum
就很好地解決了這個問題印蔬,serde_json庫的Value
就已經完美地定義了JSON的結構:
pub enum Value {
Null,
Bool(bool),
Number(Number),
String(String),
Array(Vec<Value>),
Object(Map<String, Value>),
}
沒有null
這是個老生常談的問題侥猬,null的發(fā)明者Tony Hoare將null的設計稱為十億美元錯誤,意思是null造成的無法計數的錯誤退唠、漏洞和系統(tǒng)崩潰可能已經造成了十億美元的損失荤胁。
而Rust的解決方式是通過sum type,即Option松蒜,來強制用戶必須處理null的情況:
pub enum Option<T> {
None,
Some(T),
}
在其他一些沒有sum type語言中也有類似的做法扔茅,比如dart的null safety符號?
,java的Optional
(可惜出來得太晚也不是強制性的)召娜。
賦值默認是移動語義
這個是我認為非常正確的設計,普遍的編程語言的賦值都是復制語義秸讹,對于像bool雅倒、int、指針之類的基本類型蔑匣,復制成本低,因此復制語義對于他們來說比較合適凿将,包括Rust也為基本類型提供了Copy
trait來轉換成復制語義价脾。
但是對于大型的復合體,復制語義明顯不適合,因此某些語言區(qū)分了值類型和引用類型妹孙,成了各種面試題喜歡問到的東西涕蜂。
而引用類型的本質是封裝了指針映琳,賦值的時候其實是復制的指針,指針指向的是同一份內存萨西,產生了“引用”的效果。
像Go這類存在指針的語言葱跋,那是否“引用”則清晰多了源梭,不過依然存在slice和map這類引用類型,來對抗復制語義帶來的深拷貝造成的影響荠卷。
而Rust則更加正確,同一份內存在同一時間只能被一個變量所擁有(所有權的概念)油宜,避免了復制語義帶來的影響怜姿,更加精妙的是,因為所有權的概念蚁堤,因此可以實現RAII披诗,這是Rust能夠在無gc的情況下管理內存的關鍵熟空。