於我而言,Go語言是新的Ruby

在決定重寫dnscrypt-proxy這個項目之後,作者發現即使最初用的是C語言,有一些很好的庫,但用其開發異步網絡功能仍然需要寫大量的代碼,對應的開發者社區也正在消亡。因此,作者嘗試使用Go語言進行重寫,即便被很多人反對,但作者依舊決定這麼做,並發現這是Ruby後第二個讓作者感受到編程樂趣的語言。

與Ruby結緣

我是在Ruby on Rails發佈的時候與Ruby結緣的。當時,PHP在Web開發領域無處不在,但還沒有真正意義上的框架。Rails的出現改變了遊戲規則,約定優於配置(Convention over Configuration)是一個很神奇的東西。按照約定起名字,框架知道如何讓應用程序的其他部分來訪問這些命名,不需要編寫任何多餘的代碼!使用Rails開發網站的體驗非常棒,它會幫助處理很多事情。這樣,開發人員就可以專注在描述邏輯而不是實現細節上。

比起Rails,我更喜歡Ruby。對於Ruby解釋器和編譯器開發人員來說,Ruby的語法有點糟糕。但作爲一名普通的開發人員,使用Ruby是一種樂趣,這是一種非常靈活的語言,做一件事情可以使用多種不同的方法。但如果被濫用,可能會成爲一個弱點(運行時修改會導致Ruby變成一種完全不一樣的語言),但這也是Ruby非常好的特性。

Ruby不像Rust那樣,編譯器會不停地出問題,即使你在盡力遵守制定的規則。不管怎樣,在使用Ruby時,開發人員可以用一種簡單而自然的方式表達想要做的事情。人們經常說Ruby是爲開發人員的幸福感而進行的優化,這絕對是真的,可以使用Ruby快速且毫不費勁地完成工作。Ruby不只是一門利基語言,它的可用模塊(gem)數量很驚人,想要爲開發應用程序找到合適的工具通常都不是問題。

當然,Ruby代碼的運行速度不會像其他語言那樣快。但在效率方面,Ruby是首屈一指的。一個有實際用處的應用程序比運行速度快但不成熟的應用程序更有價值。

在OpenDNS(現在是Cisco)工作期間,我幾乎什麼事情都用Ruby來完成。我是數據處理團隊的一員,我們可以自由地使用任何一門編程語言來完成任務,所以我選擇了Ruby。

Hadoop的作業也是用Ruby開發的(這要感謝JRuby)。當然,如果這些東西是用Java開的,會運行得更快,但Ruby是一種可用於快速試錯然後做出改進的語言,在這方面,Ruby是完美的。

我用Ruby在週末開發的一個項目(DNS數據庫)現在成了思科的一款核心產品,而且賣得很火。當時,我只是想基於我們現有的數據做一些實驗而已。如果我用了其他效率不那麼高的語言,我還會完成這個項目嗎?可能不會。因爲那樣的話一個週末可能不夠,而且也不可能在我的業餘時間完成這件事。

後來,我用C語言和Rust重寫了部分東西,讓運行速度更快一些,但如果沒有之前Ruby的效率和易用性,這個項目可能永遠不會存在。

那個時候,我的大多數開源項目都是用C語言或PHP開發的(還有其他一些我接觸過的語言,比如Erlang)。但我在筆記本電腦和服務器上運行的所有閉源腳本都是用Ruby編寫的。我的個人網站也是用Ruby開發的。因爲我只是想讓這些腳本做我需要做的事情,不需要花很多時間在如何編寫它們上。

試試Go語言

在Rust發佈第一個公開版的時候,我試了一把,因爲嘗試新語言總是很有趣。雖然說所有的編程語言都很糟糕,但都帶來了一些有趣的概念,這些概念可以讓你成爲更好的程序員。

從是否對開發人員友好的角度來看,Rust與Ruby是完全相反的。早期的Rust與現在的Rust有很大的不同。在OVH的時候,我還學了Scala,因爲需要開發Spark應用程序,我不得不用Scala來寫代碼。那個時候Ruby用得並不多,但仍然有很多運行在服務器端的東西是用Ruby開發的,它們不怎麼需要維護。

所以,我使用Ruby的機會越來越少,但有越來越多的機會使用Rust,儘管這門語言最終讓我變成了一個脾氣暴躁的人,完全不像使用Ruby時那樣享受寫代碼的樂趣。

有一天,我想要完全重寫dnscrypt-proxy(https://github.com/DNSCrypt/dnscrypt-proxy)。

我用C語言開發了最初的版本,但維護這些代碼非常痛苦。即使C語言有一些很好的庫,但用它開發異步網絡功能仍然需要寫大量的代碼,但獲得的效果卻很有限,而且還要很費勁地確保所有東西都能可靠、安全地運行。

有些功能已經計劃了一段時間(比如匿名DNS),但沒有時間去實現它們。C語言是一種奇妙的系統語言,但在其他方面並不是最有效率的語言。即使是增加一個很小的功能也會花費我更多的時間。

C語言的另一個缺點是開發者社區正在消亡。自從dnscrypt-proxy這個項目開始以來,用於改進代碼或添加新功能的拉取請求(PR)數量爲零。即使這個項目有一個相當大的用戶羣,包括企業用戶。

因此,我決定試試Go語言。

我沒有使用Go語言的經驗,我只知道它有“goroutine”的概念,這看起來很有趣。另外,它的編譯速度也很快。這一決定受到了dnscrypt-proxy前用戶的嚴厲批評:Go太臃腫了!看這些二進制文件有多大!看看內存使用情況!而且可能運行得很慢!

現在回想起來,選擇Go語言可能是我做過最好的決定。如果是在今天,我一定會再選它,讓我來說一下我的理由。

Go語言比Ruby更有見地。與那些鼓勵使用複雜結構來顯擺的語言不同,Go代碼簡單易讀。Go語言很穩定,而且向後兼容。因此,爲了支持新系統,或者爲了提高應用程序的速度,我只需要用新版本重新編譯就可以了。

Go編譯器的運行速度非常快。在進行快速迭代時,這個非常有用,因爲在修改少量代碼後可以立即編譯,然後進行測試。用來開發Go代碼的工具非常好用。VSCode自動添加導入(但這並不總是一個好主意,稍後會詳細介紹),爲表達式找到正確的類型,並且提供了自動完成功能(感覺非常棒,特別是對於從Rust轉過來的人來說)。Go語言的文檔非常出色,提供了很多示例,而不僅僅是內部細節。

儘管我之前沒有使用Go語言的經驗,但我很快就得到了我想要的東西。那時,dnscrypt-proxy的C語言版本已經有5年的歷史,我在一週內重寫了它的部分功能。實現一個基本的代理只需要15分鐘。

除了Go語言和相應的工具,我完成這些東西還要多虧了Go語言的可用模塊,以及它優秀的標準庫。是的,還有goroutine。

Go語言是一門非常高效的語言。我喜歡用Go語言寫代碼,因爲我可以看到應用程序按照我想要的方式演化,而不需要修改很多代碼,也不需要在添加一行代碼前花很多時間思考可不可以通過編譯。

Go語言的另一個優勢是可移植性。當然,很多語言也是高度可移植的,不過Go的庫在設計時都考慮到了可移植性,如果有必要的話可以在提供統一接口的情況下模擬所有缺失的東西。

我主要用MacBook寫代碼,但有了Go語言之後,我可以非常自信地說,相同的代碼可以在任何受支持的平臺上以完全相同的方式運行。

Go語言的交叉編譯比我見過的任何編譯器都要簡單。現在,dnscrypt-proxy的每個版本都被自動打包成23個不同的語言版本,因爲這樣做非常簡單,而且我確信它們在其他平臺上運行與在我的開發平臺上運行時的行爲是完全一樣的。

Go語言有指針,生成的代碼到處都有邊界檢查。與C語言不同,如果指針沒有被正確使用,程序會自動安全崩潰,並打印出全面的堆棧跟蹤信息。

說到堆棧跟蹤和調試,Go語言在這方面絕對是很棒的。Go語言的堆棧跟蹤信息簡短、易讀,並且包含需要的所有內容,用VSCode和delve來調試Go代碼也很棒。

Go語言的生態系統很大,無論何時需要什麼,都能找到需要的模塊。Go語言的社區很強大,每當我被問題困住的時候,總能從社區中找到答案。

我在Go語言中找到了最初在Ruby中找到的東西。Go語言是一種讓我可以用自然的方式表達想法的語言。我的應用程序迭代得很快,編程再次讓我感到興奮。

“但Go語言是有GC的!”

是的,那又怎樣?就像Java一樣,如果我需要速度,可以預先分配和重用內存來避免GC。我可以先用一種簡單而自然的方式編寫想要的代碼,然後花時間對其進行優化,避免後面出現GC暫停。

因此,Go語言的運行速度並不慢,可以FastHTTP(https://github.com/valyala/fasthttp)爲例。

“但它無法防止出現數據競爭!”

Rust迫使我以一種不自然的方式寫代碼。我發現自己花了太多時間在思考代碼是否可以通過編譯,而有些問題在運行時可能並不會發生。有時候,我也會欣賞語言的嚴格性,它迫使我以某種方式設計代碼,避免出現某些問題。但是,我也欣賞與我心智模型匹配且不會給我造成障礙的語言,特別是如果這門語言有很棒的調試工具。

Go語言的效率讓我可以更多地關注算法而不是實現。總的來說,Go語言大獲全勝。根據反饋,dnscrypt-proxy的第二個版本比C語言版本要快得多。更重要的是,它提供了大量功能,如果我沒有改用Go語言,或許就永遠不會實現這些功能。

對我來說,Go語言已經成了新的Ruby。我用它來完成工作,並再次享受編程的樂趣。

原文鏈接:

https://00f.net/2019/10/28/go-is-the-new-ruby/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章