如何看待王垠的 《對 Rust 語言的分析》?

對 Rust 語言的分析


更新,對於數組的部分,直接是錯的:

你沒有辦法制定一個不可變的數組指針,而它指向的數組的元素卻是可變的。

fn main() {
let shit = mut [1i32]; // 王垠的代碼第一個錯誤是,他的變數綁定的是數組類型,這裡按照他的原話綁定一個指向數組的指針。
shit[0] = 7;
println!("{}", shit[0]); // 會輸出 7。
shit = mut [2333] // 編譯錯誤,注釋這一行來通過編譯。注意看錯誤信息。
}

這段代碼和他描述的行為完全一致。我想不出有能辯解的點,這是最基礎的部分。

Rust 中有底層 const 和頂層 const 的區別,也就是說變數是一個 mut 和變數本身 mut 是不一樣的。

這種低級錯誤是態度問題了,我相信他沒用 Rust 寫過超過兩千、不、一千、不、五百行代碼。卻要試圖評論。讓我感覺自己在知乎評價他的文章很蠢。

---很抱歉把更新的內容放到上面,因為分割線下的內容不用看了的分割線---

很難想像王垠會覺得 () 是不重要的類型,這個類型可能是所有類型中最重要的了。

也就是說,theSky = "blue" 的所有功能應該只是「賦值」這種「副作用」,副作用不應該具有「值」。即使你牽強附會說它有一個值,它的「值」也應該是 void(隨之這個void 會被類型檢查所拒絕,因為它不是 if 所期望的 bool)。

首先 () 是有用的,可以看作一個類型佔位符,比如說對於用於錯誤處理的類型 Result& ,成功返回結果 T 失敗返回錯誤類型 E,但是對有些副作用操作,成功以後沒有返回值,就可以用 Result&<(), E&>。

更重要的是:

if (...) {
a = b
}
// if 語句塊整體為 () ,else 默認為 (),整體類型保持一致,if 有返回類型就可以出現在任何能放表達式的地方。

match x {
Foo(x) =&> y = x,
_ =&> println!("nothing"),
}
// 同理

副作用有空值,是統一有返回值和無返回值的基礎,也是能將表達式視為函數來嵌套的基礎。

其次變數被賦值了 () 以後(代表變數的類型也為 (),實際上會被優化掉 ),任意有地方試圖把它當作別的類型的變數來訪問,都會有一個編譯時類型錯誤。


對數組可變性的批評:

Rust 的數組可變性標記,跟 Swift 犯了一樣的錯誤。Swift 的問題,我已經在之前的文章有詳細敘述,所以這裡就不多說了。簡言之,同一個標記能表示的可變性,要麼針對數組指針,要麼針對數組元素,應該只能選擇其一。而在 Rust 裡面,你只有一個地方可以放「mut」進去,所以要麼數組指針和元素全部都可變,要麼數組指針和元素都不可變。你沒有辦法制定一個不可變的數組指針,而它指向的數組的元素卻是可變的。

Rust 的數組是值類型,不是引用類型,這個批評是站不住腳的。


如果一篇文章有一句讓我覺得很贊的話,我就很感激作者。我既沒興趣了解Rust語言,也沒興(neng)趣(li)追究他技術方面的吐槽是不是都正確,我只是覺得下面這段話很贊:

這個問題類似於重複綁定變數和類型推導的問題,屬於一種「用戶體驗設計」問題。無論如何,編譯器都很容易實現,然而不同樣式的代碼,對於人類閱讀的工作量,是很不一樣的。很多時候最省人力的做法並不是那種看來最聰明,最酷,打字量最少的辦法,而是寫得最明確,讓讀者省事的辦法。人們常說,代碼讀的時候比寫的時候多得多,所以要想語言好用省事,我們應該更加重視讀的時候,而不是寫的時候。


如果你想支持這篇文章的書寫,請付費5美元或者30人民幣,謝謝!

現在沒寫完就來收稅了,可見微軟工資壓的確實厲害


然而 borrow checker 乾的事情是防止人的錯誤,而不是處理它處理不了的狀況

王先生,你真能保證你能全程人肉管理內存並且一定不出錯嗎?反正以我寫 C 的經驗,程序只要超過了 10000 行,就一定會有忘記 free 的情況。


拋開王垠這個人本身不談,其實他的文章,幫助我們從另一個不同的角度來看待各個語言。

畢竟,王垠是有專業高度的,在他那個高度的視角,能看到我們一般的程序員看不到的東西。

如何看待? 抱著學習的心,辯證的去看唄!

但是具體到《對Rust語言的分析》這篇文章,我認為這是他所有文章中最爛的一篇,誤導了不少人。可以說他裡面的每個觀點都是在沒有深入了解Rust之前憑藉他「多年的經驗」的信口雌黃。


我就想知道擁鱉是個什麼鬼....


說實話,每次王垠寫技術文章,尤其是傷了「擁鱉」們的心的時候,總有人根據自己的理解或者網上隨便找的文章證明王垠是「錯誤」的,博得幾個贊同。

即使不談王垠經常使用自造術語造成的誤會也不談王垠說話的語境,這些人十有八九也是王垠口中的「民科」。各位還記得大明湖畔的彭飛嗎?

(避免誤會,這一段我是指出了王垠在知乎為什麼名聲比R大差,沒有別的意思)這一點RednaxelaFX就很不同,首先很少隨意寫負面的評論,不會招黑;其次回答里喜歡使用reference,首先就把人嚇一跳了,話題也多涉及各種複雜的工程性(也即不容易產生爭端)的問題(這也是由他的專長JIT/Java所決定的);這樣下來,R大的名聲也就好得多。

最後補充一句,數組的可變性這個王垠確實有遺漏,但是別的方面,各位知乎大神們還是省省吧。


@RednaxelaFX R大,務必要開值乎了,水垠文章性價比太低,我們需要一個健康的,選擇充分的知識市場環境。


我還是喜歡看他寫我看不懂的東西


文章里還是有不少有價值的東西,不過我覺得他沒細想:

  1. 變數
    • Rust其實標準的寫法是 let x = 8isize;
    • let聲明+類型推導最大的優點是編程思路的轉變,在於程序的核心不是變數,而是值。應該去關注等號右邊的東西,如果右邊沒有東西,甚至就不應該有變數,變數只是值的一個bind

    • 不過let mut確實比較丑,真正的mutable變數倒真是寫成Java那樣比較清楚。因為變數是要在多個地方寫的,類似於函數的返回值,要在多個地方return。kotlin語言也確實是區分兩種定義語句。不過吧,完全寫成Java那樣語法就有點不一致了。

  2. 重複綁定

    • "重複綁定」同一個名字,其實最大的作用是clone
    • 因為Rust的引用類型實在是太複雜了,為了寫出安全檢查的合格代碼,很多地方需要重新clone,而clone出來的對象語義其實和原來的對象一樣。重複綁定就是用在這裡的,除此之外不要用。Rust的檢查器clippy也不建議使用重複綁定(shadow)


注意到加了廣告,很好,有進步


最後一段寫lifetime的邏輯是:雖然我不知道rust是怎麼設計的,我也看不懂它的例子,但是我自己沒做出來,所以rust做出來的一定不好用。


再給 Rust2 年時間吧,讓最終的用戶數來決定它的命運。

我的估計是就算他活下來了,也註定是小眾的


1,關於重複綁定的,雖然沒有玩過Rust,但是我在F#某些場合常用重複綁定,譬如Option類型,判斷過非空之後,大可以使用重新綁定變數名來覆蓋原來的Option 變數,要的是一種去蕪存菁的感覺,讀代碼也不會混亂,當然也可能是習慣了這種風格,就覺得很自然,說白了就是一種思維方式,稍微鍛煉一下就可以。如果,穿上衣服是那個女人,脫了就不認,這個人實在太脆弱了。

2、unit類型,王垠沒有從實用角度去考慮,泛型中用unit佔位是必須的,不然很多泛型都要寫兩份。


類型置於變數後面毫無問題。

在我看來重複綁定唯一的意義就是把mutable重複綁定為immutable,並且用處也不大。但至少實際程序中並不會引起問題,實際中的變數不太可能是y這樣的單字母,程序員也不太會錯誤地重複使用不久前剛用過的名字。當我改動很久之前的程序時,如果定義了新的變數並且不太確定是否已經被使用,都會按一下快捷鍵確認一下當前文件里是否已經存在同樣的名字。同樣,看到let y = 4而要搜索下一次可能存在的綁定時,也不需要往下看若干行,選中「let y = 」再按一下快捷鍵就可以了。(順便我記得一個Struct里的變數和方法可以用同一個名字,這才容易引起問題,不知道為什麼會這樣設計)

關於特性的濫用,典型的例子就是多重繼承;我的觀點是只要某個特性存在一個適用的場景,就應該允許存在。不同語言不同人有不同的哲學,這個沒什麼好爭的。

類型推導也是一樣,你覺得程序不好讀可以選擇不省略。雖然我是堅決支持充分利用類型推導的

返回值為()應該是有整體設計上的考慮的,不寫返回值的函數返回值也是(),這是語言整體設計上的一致性。如果一定要返回一個值,那()是最好的選擇。實際應用中也並沒有問題,我無法想像這樣的編譯錯誤會「不大容易找到錯誤的起源」。

return 語句:首先這是程序邏輯設計的問題,if 的三個分支最好要麼都是函數出口要麼都不是。並且如果想通過最後一個語句返回,你也做不到只在其中一個分支返回,會編譯錯誤;結果還是得加return。找最後一個表達式直接從{跳轉到對應的},也不比拿編輯器搜索更麻煩。哲學的問題就不重複說了。

數組的可變性:m不是指針

內存管理:很多程序對內存管理的唯一要求就是「保證會釋放,但我會用到時還未釋放」,可以用默認的、最方便的寫法來達到內存安全而不用多管。當你需要更細節的控制,Rust也提供支持(包括裸指針),順便提醒你這時要小心謹慎。你實在覺得這程序太複雜還是用GC算了,Rust將來也很可能支持,當然換一門語言應該是更好的選擇。

當然批評現有方法(GC)的缺點是容易的,提出一個理想的目標(Rust: safe, fast, productive—pick three.)是容易的,設計出怎麼做並且做出來是困難的,也許根本就不存在這樣的好事。

edit:又想到重複綁定的一個用處。有時候會需要引入臨時變數,在Rust中由於borrow checker的存在,這樣的需求更多。而_是不能用的,那麼比如tmp就可能被使用多次並且用於不同的類型


在一個嚴格執行code review,大家都抱著想把代碼寫好的心態的團隊里,你會發現把代碼寫的簡單易懂(哪怕有時候笨一點,多寫幾行)簡直太重要了,有效減少了溝通時間。

從垠神的很多文章里能看出來,一方面,他提倡語言設計者將語言設計的簡單,另一方面,他提倡大家將代碼寫的簡單易懂。

這兩點,我覺得受用無窮。


大家對技術的正常討論就很好嘛,只要不搞人身攻擊,圍攻別人......


希望可以有機會看到王垠對rust更全面和深入的分析。


在 Yin 語言最初的設計裡面,我也是允許這樣的重複綁定的。第一個 y 和 第二個 y 是兩個不同的變數(內存空間或者寄存器),只不過它們碰巧叫同一個名字而已!這難道不是一個很酷,很靈活,其他語言都沒有的設計嗎?後來我發現,雖然這實現起來沒什麼問題,可是這樣做不但沒有帶來更大的方便性,反而可能引起程序的混淆不清。在同一個作用域裡面,給兩個不同的變數起同一個名字,這有什麼用處呢?只是自找麻煩而已。

小事不糊塗,大事必然糊塗。


推薦閱讀:

如何評價王垠新博文《我看自動駕駛技術》?
曾老師如何看待除了垠神之外其他三大編程天王?
王垠在《程序員的心理疾病》中提到 Python 的缺點是哪些?
王垠的「40 行代碼」真如他說的那麼厲害嗎?

TAG:王垠人物 | Rust編程語言 |