想學新的程式語言?考慮下Go吧

  • 2019 年 12 月 31 日
  • 筆記

作者 | Lewis Fairweather 譯者 | 彎月,責編 | Elle 來源 | CSDN(ID:CSDNnews)

【導讀】快速的運行時、高效的並發、簡單易學的語法,這些都是Go語言最吸引人的特性。

以下為譯文:

Go語言的入門門檻之低令我感到驚訝。

剛開始學習Go時,我就用它開發了一個個人項目,我強迫自己熟悉它的語法(每次學習新語言時我都會開始新的項目)。

我決定建立一個命令行應用程式,能夠列出所有子域名,幫助我的bug賞金活動。因此,該應用程式需要同時發送多個HTTP請求,有點像gobuster,但我希望重新發明這個輪子,並添加一些功能,如抓取HTML響應內容,以找到我關心的安全相關的資訊。

問題是我想用Go-routines來解決。由於需要發送的HTTP請求數是位置的,所以我需要了解怎樣才能有效地處理這些請求。

第一印象

剛上手Go語言我就覺得它的語法非常熟悉,而在這之前我甚至連文檔都沒看過。那些概念對我而言似乎非常直觀(也許別人會有不同的意見)。defer的用法也非常合理。用於解決字元串格式的fmt包似乎解決了我之前甚至不知道的問題。我開始體會到為什麼Go語言開發者要開發一門新語言。所以我決定深入到Go的最初目的,來判斷它是否值得學習。

為什麼要開發Go

  • 目標

Google開發Go語言的初衷就是讓多進程處理的開發更高效、更安全,從而改善伺服器軟體的可維護性、可靠性和可驗證性。Go語言是Google面對自己遇到的大數據處理(這也是Google目前最流行的功能之一)方面的超長編譯時間等問題交出的答卷。他們需要一種語言,該語言的重點在於可擴展性、可讀性和並發性。Go語言從誕生起就避免了其他語言必須面對的這些煩惱。Go語言的發明者們從其他語言中吸取了最需要的概念,然後進行改進,再合併到一起,形成了Go語言。例如fmt包就是個非常高效的字元串處理工具:

「fmt實現了I/O的格式化,提供了類似於C語言的printf和scanf的函數。格式的『動詞』的概念是從C語言繼承的,但要簡單得多。」

fmt就是一個從流行的成功預言(如C語言)吸取並改進的例子。

Go的並發採用了CSP的模型,使用通道(channel)避免了同步共享數據。他們認為這樣進行通訊更簡單、更安全。

另一個重點就是簡單性。Go語言需要一種有偏向性的編程風格,於是Go社區構建了這種風格額,稱為go style。這個風格貫穿所有項目,從而減少在配置lint規則和學習不同編程風格所花費的時間,這對於團隊是非常重要的。

「理論上這能夠減少開發者之間的程式碼風格和實現風格的差異,這些現象在其他語言(如JavaScript,它需要使用大量的Eslint規則)中很常見。」

  • Go語言的方法

Go語言採用的方法結合了動態類型的解釋語言的易用性,以及靜態類型的編譯語言的效率和安全性。它有int、byte和string等基本類型,也有內置的maps類型,還有指針。正交原則是Go開發中的一個重要原則,它是函數式方法的基礎。

GO使用結構(structs)表示數據,使用用戶介面來表示抽象。人們一直在爭論Go是否是面向對象的。如果你是Java開發者,那麼你第一眼很難在Go語言中看到面向對象的東西。這是因為你在尋找類型層次結構,而實際上Go的類型沒有層次結構。它只有structs,不能繼承,但卻是對象風格的。只是與繼承相比,Go更傾向於使用組合。你可以通過接受介面來實現多態。介面可以被任何符合該介面的類型對象接受。

除了這些核心概念之外,Go還意識到現代的多核心處理對於並發的需求。強並發通過goroutines和通道實現。在大型並發程式中,自動垃圾回收和高效的記憶體管理同樣重要。單元測試也非常簡單,只需在源程式碼同一目錄下使用_test.go前綴編寫文件即可。

為什麼要學習Go語言?

  • 並發

並發是刻在Go語言骨子裡的。它是一等公民,而且非常容易使用,只需要用go關鍵字給函數加上前綴即可。goroutines是低成本、輕量級的執行緒執行。在Go語言中實現並發非常簡單。只需要用go關鍵字生成一個新的執行緒,該執行緒在同一個執行緒組內可以在多個核心上共享。Goroutine只有幾KB大小,由Go運行時負責處理,它會將goroutines移動到不同的可運行的執行緒上,以避免阻塞。因此,執行是非同步的,而且非常快;幾乎和C/C++一樣快。你可以使用通道來控制goroutines的數量,儘管用起來感覺像是同步的,但實際上是非同步的。

Go運行時使用可變大小的、有界的棧,因此棧可以更小。運行時會改變存儲棧的記憶體區域的大小。同一個地址空間內可以運行幾十萬個goroutines。

  • 簡單

Go語言開發時採用了極簡方式。沒有類,也沒有集成。所有這些流行語言(如Java、Python)等的功能都用structs代替。Go是強類型、靜態類型語言。它鼓勵在任何地方使用介面。靜態類型的目標是減少編譯時錯誤。也讓語言更容易學習。

在其他語言如JavaScript中,你必須在多種方式、範式和慣例中做出抉擇,但使用Go,就只需遵守唯一的一種被所有人接受的程式碼規範。這樣,在團隊中分析和論證程式碼就非常容易,集成也會更順暢。

儘管沒有隱式轉換,但帶來的語法額外開銷依舊非常小。這樣產生的程式碼更易讀、複雜度更低。

  • 速度快

編譯器是靜態連接的。這樣在編譯生成二進位可執行文件時無需處理外部依賴。程式碼會編譯成由機器程式碼組成的可執行文件,運行時無需使用虛擬機,因此速度更快、更隨身,儘管尺寸會增大。

而且,Go語言的其他方面也很快:比如前面說過的編譯速度,以及生產環境上線的事件。Go語言專註於開發者的生產力,這主要歸功於它的簡單性。因此從初始創意到生產環境上線的速度很快。

Go語言有什麼問題?

  • 沒有泛型

這一點眾說紛紜。使用泛型的語言(如Java)能大幅度地增強程式碼的可復用性,同時保證類型安全。Go社區已經提出了這個問題,並且在考慮中。但是,Go團隊目前的態度是,泛型帶來的好處並不能超過沒有泛型時的簡單性和可閱讀性。

  • 競爭條件

「不要使用共享記憶體進行通訊,應該使用通訊來共享記憶體。」

這條原則雖然帶來了好處,但也讓Go更容易產生競爭條件。

由於Go的結構是可修改的(而且沒有不可修改的數據結構),開發者只能在多個並發進程之間共享可修改的數據。舉個例子,你可以將指針發送到通道,而不需要做深度拷貝,而數據的可修改性就可能導致競爭條件。通道也可以改善並發編程,但競爭條件的確存在,而且通道並沒有辦法防止它發生。

不過,Go CLI內置了一個競爭條件檢測器,來幫助檢測競爭條件。

  • 錯誤檢查

錯誤檢查必須顯式進行。Go語言沒有try-catch語句。因此,你必須改變錯誤處理的思路,特別是在你早已習慣了其他語言的情況下。Go語言團隊認為,不使用異常可以避免造成過度複雜的程式碼,也可以避免重載返回值。這與Go語言追求簡單時一直的。不過,在確實必要的時候你可以使用panic和recover來處理異常。而且還有個正統的error介面類型,會通過Error()返回一個錯誤字元串。

Go語言的開發者採用了多值返回值來檢查錯誤。某個可能會產生錯誤的函數可以返回一個錯誤。通常需要用if err != nil的寫法污染程式碼。

對於某些人來說可能太簡單了?

簡單是有代價的。Go不像JavaScript那樣有豐富的表現形式。Go語言沒有默認值。它還缺乏抽象,缺乏泛型,因此實現DRY原則變得非常困難、非常不直觀。

要知道的是,Go語言依然很年輕。泛型在考慮中,隨著Go語言不斷成熟,以後還會有更大的考慮空間。社區在努力開發並改進Go語言。就像任何語言一樣,Go有自己的優勢和弱點。我肯定,如果足夠多的Go程式設計師認為某個語言特性很重要,那麼這個特性肯定會被實現。

但是,儘管似乎缺乏一些語言特性,但有時候只需要從不同的角度考慮問題即可。

通常,問題總有另一種更適合Go語言的方式來解決。

什麼時候使用Go

我們可以認為,目前Go語言並不能解決所有問題,特別是與GUI有關的問題,另外還需要大量抽象的複雜系統。

但又有哪個語言能解決一切問題呢?

我們應該取其所長。如果你認為Go語言太簡單,很難用乾淨的方式增加複雜性,那麼就應該用它來構建簡單的微服務,而不是用來構建複雜的系統。使用Go構建網路工具和系統工具,而不應該用它來代替更適合某項任務的語言。

所以最重要的是,根據自己的需要選擇最合適的工具。如果Go適合你的需求,那麼就選擇Go,因為這就是Go的優勢所在。

所以現在就開始學起來吧。

原文地址:

https://medium.com/swlh/want-to-learn-a-new-programming-language-consider-go-golang-1c32dc3788ba

(*本文為AI科技大本營轉載文章,轉載請聯繫作者)