Rust 核心開發者眼中的 Go

做者介紹程序員

Nick Cameron,PingCAP 研發工程師,Rust 語言核心成員。golang


感謝 Rust 語言中文社區夥伴們的翻譯和審校:編程

  • 翻譯:尚卓燃數組

  • 審校:吳聰、張漢東安全


過去幾周,我一直在用 Go 語言編寫程序。這是我首次在大型且重要的項目中使用 Go。在研究 Rust 的特性時,我也看了不少關於 Go 的內容,包括體驗示例和編寫玩具程序。但真正用它編程又是一種徹底不一樣的體驗。
我以爲把此次體驗寫下來應該會頗有趣。在這篇文章中,我會盡可能避免將 Go 與 Rust 進行過多的比較,不過,因爲我是從 Rust 轉向 Go,不免也會包含一些比較。應該事先聲明的是,我更偏袒 Rust ,但會盡力作到客觀。

整體印象微信


用 Go 編程的感受很棒。庫程序裏有我想要的一切,整體實現較爲完善。學習體驗也十分順暢,不得不說,Go 是一種通過精心設計的實用性語言。 舉個例子:一旦你知悉了 Go 的語法,就能將其餘語言中慣用法延續到 Go 中。只要你學會一些 Go,就能夠相對輕易地推測 Go 語言的其餘特性。憑藉一些來自其餘語言的知識,我可以閱讀並理解 Go 代碼,而不須要過多的搜索(Google)。
與 C/C++、Java、Python 等相比,Go 並無那麼多痛點,並且更具生產力。然而,它仍是與這些語言處在同一個時代。儘管它從其餘語言身上吸收了一些教訓,甚至我我的認爲它多是那一代語言中最好的那個,但絕對還屬於那一代語言。這是一種漸進式的改進,而不是推陳出新(須要明確的是,這不是意味着對其價值的批判,從軟件工程的角度,漸進式改進一般會帶來好的影響)。一個很好的例證是   nil :像 Rust 和 Swift 這樣的語言已經去除了  null  的概念,而且消除了相關的一整類錯誤。Go 下降了一部分風險:沒有空值(no null values),在  nil    0  之間進行區分。但其核心思想仍未改變,一樣還會出現解空指針引用這種常見的運行時錯誤。

易學性併發


Go 很是易學。我知道人們常常吹捧這一點,可是我真的爲本身生產力的飛速提升而感到震驚。多虧了 Go 語言以及它的文檔和工具,我僅僅花了兩天時間就能夠寫出「有價值」、能夠提交的代碼。
有助於易學性的幾個因素是:
  • Go 很精簡。不少語言都試圖讓本身看起來小巧,但 Go 真正作到了這一點(這基本上是一件好事,我對這種自律精神印象深入)。app

  • 標準庫很出色(一樣,也很小)。從生態系統中尋找並使用庫程序很是容易。函數

  • 幾乎沒有其餘語言中所不具有的東西。Go 從其餘既存語言中提取了不少內容,並進行完善,最後將它們很好地組合在一塊兒。它在避免標新立異這一方面作了極大努力。工具


乏味的樣板式代碼


Go 代碼很快就會變得很是重複。 這是因爲它缺少宏或者泛型這種用於減小重複的機制(接口雖然有利於抽象,但在減小代碼重複方面做用沒有那麼大)。最終我會寫不少函數,而他們除了類型不一樣以外其餘甚至徹底同樣。
錯誤處理也會致使重複。 許多函數中像  if err != nil { return err }  這樣的樣板式代碼甚至比那些真正有價值的代碼還要多。
使用泛型或宏來減小樣板式代碼有時會受到批評,理由是不該爲使代碼易於編寫而使其喪失可讀性。我發現 Go 偏偏提供了一個反例,複製和粘貼代碼每每既快速又簡單,閱讀代碼卻會使人灰心喪氣,由於你不得不忽略大量的無關代碼或者在大量的相同代碼中找到細微的不一樣。

我喜歡的東西


  • 編譯時間:絕對快,能夠肯定要比 Rust 快得多。但實際上,它並無我預期的那麼快(對於中型到大型項目,我感受它的速度只是與 C/C++ 相接近,或者稍微快一點。而我更加期待可以即時編譯)。

  • 協程(goroutine)和信道(channel):值得稱讚的是,Go 爲生成協程和使用信道提供了輕量級的語法。儘管只是一個小細節,卻使 Go 的併發編程體驗比其餘語言更優越,它真正揭示了語法的力量。

  • 接口:它們並不複雜,可是很容易理解和使用,而且在不少地方都很實用。

  • if ...; ... { } 語法:能夠將變量的做用域限制在 if 語句真的很好。這與 Swift 及 Rust 中的 if let 起着類似的效果,但用途更爲普遍(Go 沒有像 Swift 和 Rust 那樣的模式匹配,因此它沒法使用 if let )。

  • 測試和文檔註釋都很容易使用

  • Go 工具鏈很是友好:將全部東西都放在一個地方,而不須要在命令行上使用多個工具。

  • 擁有垃圾收集器(GC):不用考慮內存管理真的會使編程更加輕鬆。

  • 可變參數


我不喜歡的東西


如下內容沒有特定的順序。
  • nil 切片:要知道 nilnil 切片和空切片三者都不相同,我敢保證咱們只須要其中的兩個,而不須要第三個。

  • 枚舉類型並非第一公民:使用常量模擬枚舉讓人感受是一種倒退。

  • 不容許循環引用:這實際上限制了包在劃分項目模塊中的可用性,由於它變相鼓勵了在一個包中堆積大量文件(或擁有大量零碎的小包,若是本該放在一塊兒的文件四處分散,這也一樣糟糕)。

  • switch 容許出現遺漏匹配的狀況

  • for ... range 語句會返回一對「索引/值」。要想只獲取索引很容易(忽略值就好);但若要只獲取值,則須要顯式聲明。在我看來,這種作法更應該顛倒過來,由於在大多數狀況下,我更須要值而不是索引。

  • 語法:

    • 定義與用途存在不一致。

    • 編譯器有時會很挑剔(例如,要求或禁止尾隨逗號);經過良好的工具能夠緩解這種困擾,可是有時仍然會產生一些惱人的額外步驟。

    • 使用多值返回類型時,類型上須要括號,但 return 語句中卻不須要。

    • 聲明一個結構體須要兩個關鍵字(type 和 struct)。

    • 採用大寫命名法來標記公共或私有變量,看起來就像匈牙利命名法那樣,但更糟糕。

  • 隱式接口。我知道它也出如今我喜歡的東西中,但有時候它確實很惹人煩——特別是當你試圖找出全部實現該接口的類型,或者哪些接口是爲給定類型而實現的時候。

  • 你沒法在不一樣的包中編寫帶有接收器的函數,因此即便接口是「鴨子類型」的,你也不能爲其餘包中的類型實現這個接口,這使得它們的用處大大下降。

還有我以前已經提過的, Go 缺乏泛型和宏

一致性


做爲一名語言設計者和程序員,Go 最讓我驚訝的地方也許是它的內置功能和用戶可用功能之間頻頻出現不一致。許多語言的目標之一就是儘量消除編譯器魔法,讓用戶也能使用內置功能。運算符重載是一個簡單但有爭議的例子。但 Go 有不少魔法!你很容易就會遇到這樣的問題:沒法作那些內置功能能夠作的事情。
一些讓我印象深入的地方:
  • 返回多個值和信道的語法很棒,可是這兩個沒法一塊兒使用,由於沒有元組類型。

  • 可以用 for ... range 語句對數組和切片進行迭代,但對其餘集合就無能爲力了,由於它缺少迭代器的概念。

  • 像 len 或者 append 這樣的函數是全局函數,但你本身的函數卻沒法轉變成全局函數。這些全局函數只能使用內置類型。即使 Go「沒有泛型」,它們也能夠變得通用。

  • 沒有運算符重載,那麼 == 就會令人感到惱火。由於這意味着你不能在詞典中使用自定義類型做爲鍵,除非它們是可比較的。這一屬性派生自類型結構,程序員沒法重寫該屬性。


總結


Go 是一種簡單、小巧、使人愉悅的語言。它也有一些犄角旮旯,但絕大部分是通過精心設計的。它的學習速度使人難以置信,而且規避了其餘語言中一些不那麼廣爲人知的特性。
Go 也是一種與 Rust 大相徑庭的語言。雖然二者均可以籠統地描述爲「系統語言」或「C 語言的替代品」,但它們的設計目標、應用領域、語言風格和優先級不盡相同。垃圾收集確實帶來了一個巨大的差別:使用 GC 使得 Go 變得更簡單、更小,也更容易理解。而不使用 GC 使 Rust 奇快無比(特別是在您須要保證延遲,而不只僅是高吞吐量的時候),而且得以支持 Go 中不可能實現的特性或編程模式(或者至少在不犧牲性能的前提下是沒法實現的)。
Go 是一種編譯型語言,其運行時獲得了良好的實現,其速度毋庸置疑。Rust 也是編譯型語言,可是運行時要小得多,它真的迅捷無比。在沒有其餘限制的狀況下,我認爲選擇使用 Go 仍是 Rust 其實意味着一種權衡: 一方面,Go 的學習曲線更短、程序更簡單 (這意味着更快的開發速度); 另外一方面,Rust 真的性能卓越,而且類型系統更富有表現力 (這使程序更安全,也意味着更快的調試和錯誤查找)。

本文分享自微信公衆號 - GoCN(golangchina)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索