此篇文章流傳甚廣, 其實裏面沒啥乾貨, 並且裏面不少觀點是有問題的. 這個文章在 golang-china 很早就討論過了. 最近由於 Rust 1.0 和 1.1 的發佈, 致使這個文章又出來毒害讀者. 因此寫了這篇反駁文章, 指出其中的問題.javascript
原文連接:http://blog.csdn.net/liigo/article/details/23699459php
有好幾回,當我想起來的時候,老是會問本身:我爲何要放棄Go語言?這個決定是正確的嗎?是明智和理性的嗎?其實我一直在認真思考這個問題。java
開門見山地說,我當初放棄Go語言(golang),就是由於兩個「不爽」:第一,對Go語言自己不爽;第二,對Go語言社區裏的某些人不爽。毫無疑問,這是很是主觀的結論。可是我有足夠詳實的客觀的論據,用以支撐這個看似主觀的結論。nginx
文末附有本文更新日誌。c++
確實是很是主觀的結論, 由於裏面有很多有問題的觀點(用來忽悠Go小白還行).git
第0節:個人Go語言經歷
先說說個人經歷吧,以免被平白無故地看成Go語言的低級黑。程序員
2009年末,Go語言(golang)第一個公開版本發佈,籠罩着「Google公司製造」的光環,吸引了許多慕名而來的嚐鮮者,我(Liigo)也身居其中,籠統的看了一些Go語言的資料,學習了基礎的教程,因對其語法中的分號和花括號不滿,很快就遺忘掉了,沒拿它當一回事。github
在2009年Go剛發佈時, 確實是由於「Google公司製造」的光環而吸引了(包括文章做者和諸多IT記者)不少低級的嚐鮮者. 還好, 通過5年的發展, 這些純粹由於光環來的投機者所剩已經很少了(Google趨勢). 目前, 真正的Go用戶早就將Go用於實際的生產了.golang
說到 其語法中的分號和花括號不滿, 我想說這只是你的 我的主觀感覺, 還有不少人對Go的分號和花括號很滿意, 包括水果公司的的 Swift 的語言設計者也很滿意這種風格(Swift中的分號和花括號和Go基本相同).正則表達式
若是隻談 我的主觀感覺, 我也能夠說 Rust 的 fn
縮寫也很蛋疼!
兩年以後,2011年末,Go語言發佈1.0的計劃被提上日程,相關的報道又多起來,我再次關注它,從新評估以後決定深刻參與Go語言。我訂閱了其users、nuts、dev、commits等官方郵件組,堅持天天閱讀其中的電子郵件,以及開發者提交的每一次源代碼更新,給Go提交了許多改進意見,甚至包括修改Go語言編譯器源代碼直接參與開發任務。如此持續了數月時間。
這個到是事實, 在 golang-china 有很多吵架的帖子, 感興趣的能夠去挖下, 我就不展開說了.
到2012年初,Go 1.0發佈,語言和標準庫都已經基本定型,不可能再有大幅改進,我對Go語言未能在1.0定型以前更上一個臺階、實現自我突破,甚至帶着諸多明顯缺陷走向1.0,感到很是失望,於是逐漸疏遠了它(因此Go 1.0以後的事情我不多關心)。後來看到即將發佈的Go 1.1的Release Note,發現語言層面沒有太大改變,只是在庫和工具層面有所修補和改進,感到它尚在幼年就失去成長的動力,愈加失望。外加Go語言社區裏的某些人,其中也包括Google公司負責開發Go語言的某些人,其態度、言行,讓我極度厭惡,促使我決絕地離棄Go語言。
真的不清楚樓主說的能夠在 Go1.0 以前短期內能實現的 重大改進和諸多明顯缺陷 是什麼.
若是是樓主說前面的 其語法中的分號和花括號不滿 之類的重大改進, 我只能說這只是你的 我的主觀感覺 而已, 你的不少想法只能說服你本身, 沒辦法說服其餘絕大部分人(不要覺得像C++或Rust那樣什麼特性都有就NB了, 各類NB特性加到一塊兒只能是 要你命3000, 而絕對不會是什麼 銀彈).
Go 1.1的Release Note,發現語言層面沒有太大改變. 語言層沒有改變是是由於 Go1 做出的向後兼容的承諾. 對於工業級的語言來講, Go1 這個只能是優勢. 若是連語言層在每一個版本都會出現諸多大幅改進, 那誰還敢用Go語言來作生產開發呢(我認可Rust的改動很大膽, 但也說明了Rust還處於比較幼稚和任性的階段)?
說 Go語言社區裏的某些人執拗 的觀點我是贊成的. 可是這些 執拗 的人是能夠講道理的, 可是他們對不少東西的要求很高(特別是關於Go的設計哲學部分). 只要你給的建議有依據(語言的設計哲學是另一回事情), 他們絕對不會盲目的拒絕(只是討論的週期會比較長).
關於樓主提交的給Go文件添加BOM的文章, 須要補充說明下.
在Go1.0發佈的時候, Go語言的源文件(.go
)明確要求必須是UTF8編碼的, 並且是無BOM的UTF8編碼的(G公司的Protobuf也不支持帶BOM的UTF8編碼).
注意: 這個 無BOM的UTF8編碼 的限制僅僅是 針對 Go語言的源文件(.go
).
這個限制並非說不容許用戶處理帶BOM的UTF8的txt文件!
我以爲對於寫Go程序來講, 這個限制是沒有任何問題的, 到目前爲止, 我還歷來沒有使用過帶BOM的.go
文件.
不只是由於帶BOM的.go
文件沒有太多的意義, 並且有不少的缺陷.
BOM的原意是用來表示編碼是大端仍是小端的, 主要用於UTF16和UTF32. 對於 UTF8 來講, BOM 沒有任何存在的意義(正是Go的2個做者發明了UTF8, 完全解決了全球的編碼問題).
可是, 在現實中, 由於MS的txt記事本, 對於中文環境會將txt(甚至是C/C++源文件)看成GBK編碼(GBK是個爛編碼), 爲了區別究竟是GBK仍是UTF8, MS的記事本在前面加了BOM這個垃圾(被GBK佔了茅坑), 這裏的bom已經不是表示字節序本意了. 不知道有沒有人用ms的記事本寫網頁, 而後生成一個帶bom的utf8網頁確定頗有意思. 這是MS的記事本的BUG: 它不支持生成無BOM的UTF8編碼的文本文件!
這些是現實存在的帶BOM的UTF8編碼的文本文件, 可是它們確定都不是Go語言源文件!
因此說, Go語言的源文件即便強制限制了無BOM的UTF8編碼要求, 也是沒有任何問題的(並且我還但願有這個限制).
雖而後來Go源文件接受帶BOM的UTF8了, 可是運行 go fmt
以後, 仍是會刪除掉BOM的(由於BOM就是然並卵). 也就是說 帶 BOM 的 Go 源文件是不符合 Go語言的編碼風格的, go fmt
會強制刪除 BOM 頭.
前面說了BOM是MS帶來的垃圾, 可是BOM的UTF8除了然並卵以外還有不少問題, 由於BOM在string的開頭嵌入了垃圾, 致使正則表達式, string的連接運算等操做都被會被BOM這個垃圾所污染. 對於.go
語言, 即便代碼徹底同樣, 有BOM和無BOM會致使文件的MD5之類的校驗碼不一樣.
因此, 我以爲Go用戶不用糾結BOM這個可有可無的東西(語言源文件不是文本編輯器, 不必支持各類文件格式).
在上一個10年,我(Liigo)在我所屬的公司裏,深度參與了兩個編程語言項目的開發。我想,對於如何判斷某個編程語言的優劣,或者說至少對於如何判斷某個編程語言是否適合於我本身,我應該仍是有一點發言權的。
第1節:我爲何對Go語言不爽?
Go語言有不少讓我不爽之處,這裏列出我如今還能記起的其中一部分,排名基本上不分前後。讀者們耐心地看完以後,還能淡定地說一句「我不在意」嗎?
1.1 不容許左花括號另起一行
關於對花括號的擺放,在C語言、C++、Java、C#等社區中,十餘年來存在持續爭議,從未造成一致意見。在我看來,這原本就是主觀傾向很重的抉擇,不違反原則不涉及是非的狀況下,不該該搞一刀切,讓程序員或團隊本身選擇就足夠了。編程語言自己強行限制,把本身的喜愛強加給別人,得不償失。不管傾向於其中任意一種,必然得罪與其對立的一羣人。雖然我如今已經習慣了把左花括號放在行尾,但一想到被禁止其餘選擇,就感到十分不爽。Go語言這這個問題上,沒有作到「團結一切能夠團結的力量」不說,還有意給本身樹敵,太失敗了。
我以爲Go最偉大的發明是 go fmt
, 今後Go用戶不會再有花括弧的位置這種無聊爭論了(固然也少了很多灌水和上tiobe排名的機會). 只給用戶一條路,不給任何走歧途的機會, 確保正確、高效。
是這優勢, Swift 語言也使用和 Go 相似的風格(固然樓主也可能鄙視swift的做者).
1.2 編譯器莫名其妙地給行尾加上分號
對Go語言自己而言,行尾的分號是能夠省略的。可是在其編譯器(gc)的實現中,爲了方便編譯器開發者,卻在詞法分析階段強行添加了行尾的分號,反過來又影響到語言規範,對「怎樣添加分號」作出特殊規定。這種變態作法前無古人。在左花括號被意外放到下一行行首的狀況下,它自動在上一行行尾添加的分號,會致使莫名其妙的編譯錯誤(Go 1.0以前),連它本身都解釋不明白。若是實在處理很差分號,乾脆不要省略分號得了;或者,Scala和JavaScript的編譯器是開源的,跟它們學學怎麼處理省略行尾分號能夠嗎?
又是樓主的 我的主觀感覺, 不過我很喜歡這個特性. Swift 語言也是相似.
1.3 極度強調編譯速度,不惜放棄本應提供的功能
程序員是人不是神,編碼過程當中免不了由於大意或疏忽犯一些錯。其中有一些,是你們集體性的很容易就中招的錯誤(Go語言裏的例子我暫時想不起來,C++裏的例子有「基類析構函數不是虛函數」)。這時候編譯器應該站出來,多作一些檢查、約束、覈對性工做,儘可能阻止常規錯誤的發生,儘可能不讓有潛在錯誤的代碼編譯經過,必要時給出一些警告或提示,讓程序員留意。編譯器不就是機器麼,不就是應該多作髒活累活雜活、減小人的心智負擔麼?編譯器多作一項檢查,可能會避免數十萬程序員從此多年內無數次犯一樣的錯誤,節省的時間不可勝數,這是功德無量的好事。可是Go編譯器的做者們可不這麼想,他們不肯意本身多花幾個小時給編譯器增長新功能,以爲那是虧本,反而減慢了編譯速度。他們以影響編譯速度爲由,拒絕了不少對編譯器改進的要求。典型的因噎廢食。強調編譯速度當然值得讚揚,但若是所以放棄應有的功能,我不同意。
編譯速度是很重要的, 若是編譯速度夠慢, 語言再好也不會有人使用的. 好比C/C++的增量編譯/預編譯頭文件/併發編譯都是爲了提升編譯速度. Rust1.1 也號稱 比 1.0 的編譯時間減小了32% (注意: 不是運行速度).
固然, Go剛面世的時候, 編譯速度是其中的一個設計目標.
不過我想樓主, 可能想說的是由於編譯器本身添加分號而致使的編譯錯誤的問題. 我以爲Go中 {
不能另起一行是語言特性, 若是修復這個就是引入了新的錯誤.
其餘的我真想不起來還有哪些 調編譯速度,不惜放棄本應提供的功能 (不要提泛型, 那是由於尚未好的設計).
最重要是的保持Compiler的靠譜、精簡、高效,而不是功能花哨,bug一堆。這樣有利於作流水優化、指令集精減、易於跨平臺、下降維護負擔。
1.4 錯誤處理機制太原始
在Go語言中處理錯誤的基本模式是:函數一般返回多個值,其中最後一個值是error類型,用於表示錯誤類型極其描述;調用者每次調用完一個函數,都須要檢查這個error並進行相應的錯誤處理:if err != nil { /這種代碼寫多了不想吐麼/ }。此模式跟C語言那種很原始的錯誤處理相好比出一轍,並沒有實質性改進。實際應用中很容易造成多層嵌套的if else語句,能夠想想這個編碼場景:先判斷文件是否存在,若是存在則打開文件,若是打開成功則讀取文件,若是讀取成功再寫入一段數據,最後關閉文件,別忘了還要處理每一步驟中出現錯誤的狀況,這代碼寫出來得有多變態、多醜陋?實踐中廣泛的作法是,判斷操做出錯後提早return,以免多層花括號嵌套,但這麼作的後果是,許多錯誤處理代碼被放在前面突出的位置,常規的處理邏輯反而被掩埋到後面去了,代碼可讀性極差。並且,error對象的標準接口只能返回一個錯誤文本,有時候調用者爲了區分不一樣的錯誤類型,甚至須要解析該文本。除此以外,你只能手工強制轉換error類型到特定子類型(靜態類型的優點沒了)。至於panic - recover機制,致命的缺陷是不能跨越庫的邊界使用,註定是一個半成品,最多隻能在本身的pkg裏面玩一玩。Java的異常處理雖然也有自身的問題(好比Checked Exceptions),但整體上仍是比Go的錯誤處理高明不少。
話說, 軟件開發都發展了半個世紀, 仍是無實質性改進. 不要覺得弄一個異常的語法糖就是革命了.
我只能說錯誤和異常是2個不一樣的東西, 將全部錯誤看成異常那是SB行爲.
try..catch原理是jump/longjump,這種東西會增長底層複雜性,而且容易濫用,很差維護,並且可能會增長10W數量級別gorutine上下文swich負擔。
正由於有異常這個所謂的銀彈, 致使不少等着別人幫忙擦屁股的行爲(注意 shit
函數拋出的絕對不會是一種類型的 shit
, 而被其間接調用的各類 xxx_shit
也可能拋出各類類型的異常, 這就致使 catch
失控了):
int main() { try { shit(); } catch( /* 到底有幾千種 Exception ? */) { ... } }
Go的建議是 panic - recover 不跨越邊界, 也就是要求正常的錯誤要由pkg的處理掉. 這是負責任的行爲.
再說Go是面向併發的編程語言, 在海量的 goroutine 中使用 try/catch
是否是有一種不三不四的感受呢?
1.5 垃圾回收器(GC)不完善、有重大缺陷
在Go 1.0前夕,其垃圾回收器在32位環境下有內存泄漏,一直拖着不願改進,這且不說。Go語言垃圾回收器真正致命的缺陷是,會致使整個進程不可預知的間歇性停頓。像某些大型後臺服務程序,如遊戲服務器、APP容器等,因爲佔用內存巨大,其內存對象數量極多,GC完成一次回收週期,可能須要數秒甚至更長時間,這段時間內,整個服務進程是阻塞的、停頓的,在外界看來就是服務中斷、無響應,再牛逼的併發機制到了這裏通通失效。垃圾回收器按期啓動,每次啓動就致使短暫的服務中斷,這樣下去,還有人敢用嗎?這但是後臺服務器進程,是Go語言的重點應用領域。以上現象可不是我假設出來的,而是事實存在的現實問題,受其嚴重困擾的也不是一家兩家了(2013年末ECUG Con 2013,京東的劉奇提到了Go語言的GC、defer、標準庫實現是性能殺手,最大的痛苦是GC;美團的沈鋒也提到Go語言的GC致使後臺服務間隔性停頓是最大的問題。更早的網絡遊戲仙俠道開發團隊也曾受Go垃圾回收的沉重打擊)。在實踐中,你必須努力減小進程中的對象數量,以便把GC致使的間歇性停頓控制在可接受範圍內。除此以外你別無選擇(難道你還想本身更換GC算法、甚至砍掉GC?那仍是Go語言嗎?)。跳出圈外,我近期一直在思考,必定須要垃圾回收器嗎?沒有垃圾回收器就必定是歷史的倒退嗎?(可能會新寫一篇博客文章專題探討。)
這是說的是32位系統, 這絕對不是Go語言的重點應用領域!! 我能夠說Go出生就是面向64位系統和多核心CPU環境設計的. (再說 Rust 目前好像還不支持 XP 吧, 這可不能夠算是影響巨大?)
32位當時是有問題, 可是對實際生產影響並不大(請問樓主仍是在用32位系統嗎, 還只安裝4GB的內存嗎). 若是是8位單片機環境, 建議就不要用Go語言了, 直接C語言好了.
並且這個問題早就不存在了(你們能夠去看Go的發佈日誌).
Go的出生也就5年時間, GC的完善和改進是一個持續的工做, 2015年8月將發佈的 Go1.5將採用並行GC, 每次 "stop the world" 時間低於 10 毫秒, 具體請參考 GopherCon2015: Go GC: Solving the Latency Problem in Go 1.5.
關於GC的被人詬病的地方是會致使卡頓, 可是我覺得這個主要是由於GC的實現還不夠完美而致使的. 若是是完美的併發和增量的GC, 那應該不會出現大的卡頓問題的.
固然, 若是非要實時性, 那用C好了(實時並不表示性能高, 只是響應時間可控).
對於Rust之類沒有GC的語言來講, 想很方便的開發併發的後臺程序那幾乎是不可能的.
不要老是吹Rust能代替底層/中層/上層的開發, 咱們要看有誰用Rust真的作了什麼.
1.6 禁止未使用變量和多餘import
Go編譯器不容許存在被未被使用的變量和多餘的import,若是存在,必然致使編譯錯誤。可是現實狀況是,在代碼編寫、重構、調試過程當中,例如,臨時性的註釋掉一行代碼,很容易就會致使同時出現未使用的變量和多餘的import,直接編譯錯誤了,你必須相應的把變量定義註釋掉,再翻頁回到文件首部把多餘的import也註釋掉,……等事情辦完了,想把剛纔註釋的代碼找回來,又要好幾個麻煩的步驟。還有一個讓人蛋疼的問題,編寫數據庫相關的代碼時,若是你import某數據庫驅動的pkg,它編譯給你報錯,說不須要import這個未被使用的pkg;但若是你聽信編譯器的話刪掉該import,編譯是經過了,運行時必然報錯,說找不到數據庫驅動;你看看程序員被折騰的兩邊不是人,最後不得不請出大神:
import _
。對待這種問題,一個比較好的解決方案是,視其爲編譯警告而非編譯錯誤。可是Go語言開發者很執拗,不允許這種折中方案。
這個問題我只能說樓主的吐槽真的是沒水平.
爲什麼不使用的是錯誤而不是警告? 這是爲了將低級的bug消滅在編譯階段(你們能夠想下C/C++的那麼多警告有什麼卵用).
並且, import
即便沒有使用的話, 是用反作用的, 由於 import
會致使多個init函數
和全局變量的初始化,致使程序不可控. 若是某些代碼沒有使用, 爲什麼要執行 init
這些初始化呢?
若是是由於調試而添加的變量, 那麼調試完刪除不是很正常的要求嗎?
若是是由於調試而要導入fmt
或log
之類的包, 刪除調試代碼後又致使 import
錯誤的花, 樓主難道不知道在一個獨立的文件包裝下相似的輔助調試的函數嗎?
import (
"fmt" "log" ) func logf(format string, a ...interface{}) { file, line := callerFileLine() fmt.Fprintf(os.Stderr, "%s:%d: ", file, line) fmt.Fprintf(os.Stderr, format, a...) } func fatalf(format string, a ...interface{}) { file, line := callerFileLine() fmt.Fprintf(os.Stderr, "%s:%d: ", file, line) fmt.Fprintf(os.Stderr, format, a...) os.Exit(1) }
import _
是有明確行爲的用法, 就是爲了執行包中的 init
等函數(能夠作某些註冊操做).
將警告看成錯誤是Go的一個哲學, 固然在樓主看來這是白癡作法.
1.7 建立對象的方式太多使人糾結
建立對象的方式,調用new函數、調用make函數、調用New方法、使用花括號語法直接初始化結構體,你選哪種?很差選擇,由於沒有一個固定的模式。從實踐中看,若是要建立一個語言內置類型(如channel、map)的對象,一般用make函數建立;若是要建立標準庫或第三方庫定義的類型的對象,首先要去文檔裏找一下有沒有New方法,若是有就最好調用New方法建立對象,若是沒有New方法,則退而求其次,用初始化結構體的方式建立其對象。這個過程頗爲周折,不像C++、Java、C#那樣直接new就好了。
C++的new
是狗屎. new
致使的問題是構造函數和普通函數的行爲不一致, 還有加不加(),行爲不一致, 這個補丁特性真的沒啥優越的.
我仍是喜歡C語言的 fopen
和 malloc
之類構造函數, 構造函數就是普通函數, Go語言中也是這樣.
C++中, 除了構造不兼容普通函數, 析構函數也是不兼容普通函數. 這個而引入的坑有不少吧.
1.8 對象沒有構造函數和析構函數
沒有構造函數還好說,畢竟還有自定義的New方法,大體也算是構造函數了。沒有析構函數就比較難受了,無法實現RAII。額外的人工處理資源清理工做,無疑加劇了程序員的心智負擔。沒人性啊,還嫌咱們程序員加班還少嗎?C++裏有析構函數,Java裏雖然沒有析構函數可是有人家finally語句啊,Go呢,什麼都沒有。沒錯,你有個defer,但是那個defer問題更大,詳見下文吧。
defer
能夠覆蓋析構函數的行爲, 固然 defer
還有其餘的任務. Swift2.0 也引入了一個簡化版的 defer
特性.
1.9 defer語句的語義設定不甚合理
Go語言設計defer語句的出發點是好的,把釋放資源的「代碼」放在靠近建立資源的地方,但把釋放資源的「動做」推遲(defer)到函數返回前執行。遺憾的是其執行時機的設置彷佛有些不甚合理。設想有一個須要長期運行的函數,其中有無限循環語句,在循環體內不斷的建立資源(或分配內存),並用defer語句確保釋放。因爲函數一直運行沒有返回,全部defer語句都得不到執行,循環過程當中建立的大量短暫性資源一直積累着,得不到回收。並且,系統爲了存儲defer列表還要額外佔用資源,也是持續增長的。這樣下去,過不了多久,整個系統就要由於資源耗盡而崩潰。像這類長期運行的函數,http.ListenAndServe()就是典型的例子。在Go語言重點應用領域,能夠說幾乎每個後臺服務程序都必然有這麼一類函數,每每還都是程序的核心部分。若是程序員不當心在這些函數中使用了defer語句,能夠說後患無窮。若是語言設計者把defer的語義設定爲在所屬代碼塊結束時(而非函數返回時)執行,是否是更好一點呢?但是Go 1.0早已發佈定型,爲了保持向後兼容性,已經不可能改變了。當心使用defer語句!一不當心就中招。
前面說到 defer
還有其餘的任務, 也就是 defer
中執行的 recover
能夠捕獲 panic
拋出的異常. 還有 defer
能夠在 return
以後修改命名的返回值.
上面2個工做要求 defer
只能在函數退出時來執行.
樓主說的 defer
是相似 Swift2.0 中 defer
的行爲, 可是 Swift2.0 中 defer
是沒有前面2個特性的.
Go中的defer
是以函數做用域做爲觸發的條件的, 是會致使樓主說的在 for
中執行的錯誤用法(哪一個語言沒有坑呢?).
不過 for
中 局部 defer
也是有辦法的 (Go中的defer
是以函數做用域):
for { func(){ f, err := os.Open(...) defer f.Close() }() }
在 for
中作一個閉包函數就能夠了. 本身不會用不要怪別人沒告訴你.
Swift 的塊級 defer
也不方便實現如下的場景:
func (t *T) Serve() {
if debug { log.Println(t, "starting") defer log.Println(t, "exiting") } // stuff }
Nigel Tao 給的 解釋:
The longer answer is that while there's benefit of a scope-scoped defer, there's also benefit in a function-scoped defer. This code: func foo(filename string) error { var r io.Reader if filename != "" { f, err := os.Open(filename) if err != nil { return err } defer f.Close() r = f } else { r = strings.NewReader(fakeInput) } // More code that reads from r. etc }
1.10 許多語言內置設施不支持用戶定義的類型
for in、make、range、channel、map等都僅支持語言內置類型,不支持用戶定義的類型(?)。用戶定義的類型無法支持for in循環,用戶不能編寫像make、range那樣「參數類型和個數」甚至「返回值類型和個數」均可變的函數,不能編寫像channel、map那樣相似泛型的數據類型。語言內置的那些東西,到處充斥着斧鑿的痕跡。這體現了語言設計的侷限性、封閉性、不完善,可擴展性差,像是新手做品——且不論其設計者和實現者如何權威。延伸閱讀:Go語言是30年前的陳舊設計思想,用戶定義的東西幾乎都是二等公民(Tikhon Jelvis)。
說到底, 這個是由於對泛型支持的不完備致使的. 記得1.5之後能夠自定義strct來支持 for,channel, map等。
Go語言是沒啥NB的特性, 可是Go的特性和工具組合在一塊兒就是好用.
這就是Go語言NB的地方.
1.11 沒有泛型支持,常見數據類型接口醜陋
沒有泛型的話,List、Set、Tree這些常見的基礎性數據類型的接口就只能很醜陋:放進去的對象是一個具體的類型,取出來以後成了無類型的interface{}(能夠視爲全部類型的基礎類型),還得強制類型轉換以後才能繼續使用,使人無語。Go語言缺乏min、max這類函數,求數值絕對值的函數abs只接收/返回雙精度小數類型,排序接口只能藉助sort.Interface無奈的迴避了被比較對象的類型,等等等等,都是沒有泛型致使的結果。沒有泛型,接口很難優雅起來。Go開發者沒有明確拒絕泛型,只是說尚未找到很好的方法實現泛型(能不能學學已經開源的語言呀)。現實是,Go 1.0已經定型,泛型尚未,那些醜陋的接口爲了保持向後兼容必須長期存在着。
Go有本身的哲學, 若是能有和目前哲學不衝突的泛型實現, 他們是不會反對的.
若是隻是簡單學學(或者叫抄襲)已經開源的語言的語法, 那是C++的設計風格(或者說C++歷來都是這樣設計的, 有什麼特性就抄什麼), 致使了各類腦裂的編程風格.
編譯時泛型和運行時泛型多是沒法徹底兼容的, 看這個例子:
type Adder<T> interface { Add(a, b T) T }
請問 Adder<int>
和 Adder<float>
是一個接口嗎?
type Adder interface { Add(a, b interface{}) interface{} }
對於這種場景, interface{}
雖然性能不是最好, 可是接口倒是一致的:
並且, 目前已經有 go generate
能夠彌補範型和宏部分的不足.
golang-china 關於該文的討論中有涉及到泛型的討論.
感受Go即便真有泛型, 也得等到Go2.0了(猜想Go2.0能在2020年誕生10週年發佈).
1.12 實現接口不須要明確聲明
這一條一般是被看成Go語言的優勢來宣傳的。可是也有人不贊同,好比我。若是一個類型用Go語言的方式默默的實現了某個接口,使用者和代碼維護者都很難發現這一點(除非仔細覈對該類型的每個方法的函數簽名,並跟全部可能的接口定義相互對照),天然也想不到與該接口有關的應用,顯得十分隱晦,不直觀。支持者可能會辯解說,我能夠在文檔中註明它實現了哪些接口。問題是,寫在文檔中,還不如直接寫到類型定義上呢,至少還能獲得編譯器的靜態類型檢查。缺乏了編譯器的支持,當接口類型的函數簽名被改變時,當實現該接口的類型方法被無心中改變時,實現者可能很難意識到,該類型實現該接口的隱含約束事實上已經被打破了。又有人辯解說,我能夠經過單元測試確保類型正確實現了接口呀。我想說的是,明明能夠經過明確聲明實現接口,享受編譯器提供的類型檢查,你卻要本身找麻煩,去寫本來多餘的單元測試,找虐很爽嗎?Go語言的這種作法,除了減小一些對接口所在庫的依賴以外,沒有其餘好處,得不償失。延伸閱讀:爲何我不喜歡Go語言式的接口(老趙)。
Go是面向組合的, 和UNIX的哲學相似. 使用Go你要知道 io
放的是什麼, fmt
包放的是什麼, 習慣以後會很方便.
你不能說UNIX的命令行工具sort
沒有實現強的接口依賴檢測會有不少問題. 若是你非要亂用sort
的搗蛋話固然有不少問題.
可是Go給想組合和合做的人使用的,組合優於繼承.
不要提 老趙 那個文章了, 我發了反駁文章後他已經閉嘴了: http://my.oschina.net/chai2010/blog/122400
對於IDE環境, Go的工具 go oracle
能夠回答某類型實現了哪些接口這類問題.
1.13 省掉小括號卻省不掉花括號
Go語言裏面的if語句,其條件表達式不須要用小括號擴起來,這被做爲「代碼比較簡潔」的證據來宣傳。但是,你省掉了小括號,卻不能省掉大括號啊,一條完整的if語句至少還得三行吧,人家C、C++、Java均可以在一行以內搞定的(能夠省掉花括號)。人家還有x?a:b表達式呢,也是一行搞定,你Go語言用if else寫至少得五行吧?哪裏簡潔了?
「代碼比較簡潔」, 誰告訴你是這個緣由了? 不懂別瞎說!
必須花括弧的緣由是C語言中 if else
的懸掛問題:
if(1) ....; if(2) ...; else ...;
請問上面的 else
是屬於哪一個 if
的?
必須加花括弧能夠避免上面的問題.
而小括弧又不是必須的所以就去掉了(Swift一樣用了Go的設計).
至於 x?a:b
雖然是簡潔, 可是容易氾濫 (x?a:b)?(x?a:b):(x?a:(x?a:(...)))
.
Go不是由於 簡潔 的 x?a:b
而禁止三元操做符, 而是爲了防止氾濫使用而禁止三元操做符.
1.14 編譯生成的可執行文件尺寸很是大
記得當年我寫了一個很簡單的程序,把全部系統環境變量的名稱和值輸出到控制檯,核心代碼也就那麼三五行,結果編譯出來把我嚇壞了:EXE文件的大小超過4MB。若是是C語言寫的一樣功能的程序,0.04MB都是多的。我把這個信息反饋到官方社區,結果人家不在意。是,我知道如今的硬盤容量都數百GB、上TB了……可您這種優化程度……怎麼讓我相信您在其餘地方也能作到不錯呢。(再次強調一遍,我全部的經驗和數據都來自Go 1.0發佈前夕。)
C語言的0.04MB程序若是崩了(Windows64環境TDM-GCC生成128KB), 你就只能知道它崩了.
而Go1.0的4MB程序若是崩了, 你能夠知道在哪一個文件的哪行代碼崩了, 這就是差異!
對於Go1.5, Windows64環境, 使用 fmt.Println
, Hello world
生成的 exe 有 2.4 MB.
對於 Rust1.1, Windows64環境, 生成的 exe 有 2.3 MB.
作了一個數組越界致使崩潰的測試, Go生成的2.4MB的程序能夠輸出致使崩潰的文件名和行號:
panic: runtime error: index out of range goroutine 1 [running]: main.main() D:/path/to/main.go:7 +0x1b9
至關於CXX/C 加 -g -O 參數使生成文件含debug/trace信息,這樣會增長文件大小。-w 去掉DWARF調試信息,獲得的程序就不能用gdb調試了
不建議s和w同時使用。也能夠壓縮生成文件。
Rust 生成的exe只能輸出如下沒啥用的信息:
thread '<main>' panicked at 'index out of bounds: the len is 2 but the index is 100', C:/bot/slave/stable-dist-rustc-win-gnu-64/build/src/libcollections\vec.rs: 1359
關於exe大小的問題能夠關注 Issue6853.
1.15 不支持動態加載類庫
靜態編譯的程序固然是很好的,沒有額外的運行時依賴,部署時很方便。可是以前咱們說了,靜態編譯的文件尺寸很大。若是一個軟件系統由多個可執行程序構成,累加起來就很可觀。若是用動態編譯,發佈時帶同一套動態庫,能夠節省不少容量。更關鍵的是,動態庫能夠運行時加載和卸載,這是靜態庫作不到的。還有那些LGPL等協議的第三方C庫受版權限制是不容許靜態編譯的。至於動態庫的版本管理難題,能夠經過給動態庫內的全部符號添加版本號解決。不管如何,應該給予程序員選擇權,讓他們本身決定使用靜態庫仍是動態庫。一刀切的拒絕動態編譯是不合適的。
假設系統由100多個exe組成了, 那總共也就是不超過1GB的磁盤空間, 沒以爲有多大.
並且DLL依賴的地獄難道忘記了嗎.
Go 1.8及之後支持plugin, 還能夠玩玩hot-plugin,https://github.com/campoy/golang-plugins。
1.16 其餘
- 不支持方法和函數重載(overload)
-
導入pkg的import語句後邊部分居然是文本(import 」fmt」)
-
沒有enum類型,全局性常量難以分類,iota把簡單的事情複雜化
-
定義對象方法時,receiver類型應該選用指針仍是非指針讓人糾結
-
定義結構體和接口的語法稍繁,interface XXX{} struct YYY{} 不是更簡潔嗎?前面加上type關鍵字顯得羅嗦。
-
測試類庫testing裏面沒有AssertEqual函數,標準庫的單元測試代碼中充斥着if a != b { t.Fatal(...) }。
-
語言太簡單,以致於不得不放棄不少有用的特性,「保持語言簡單」每每成爲拒絕改進的理由。
-
標準庫的實現整體來講不甚理想,其代碼質量大概處於「基本可用」的程度,真正到企業級應用領域,每每就會暴露出諸多不足之處。
-
版本都發展到1.2了,goroutine調度器依舊默認僅使用一個系統線程。GOMAXPROCS的長期存在彷佛暗示着官方歷來沒有足夠的信心,讓調度器正確安全地運行在多核環境中。這跟Go語言自身以併發爲核心的定位有致命的矛盾。(直到2015年下半年1.5發佈後纔有改觀)
-
官方發行版中包含了一個叫oracle的輔助程序,與Oracle數據庫毫無關係,卻徹底無視二者之間的名稱混淆。
-
不支持函數重載減輕了讀代碼的負擔, 是好事情. 可變的
a+b
行爲比可變的Add(a,b)
難發現多了 -
import
導入文本絕對是優勢, 由於能夠支持不少以特殊字符命名的路徑:import "_-aa/bb~/dd/xx"
, 只有包名知足ID命名規則就能夠了, 前綴部分能夠很隨意 -
receiver
就是普通函數:func(self T, ...)
和func(self *T, ...)
的差異不是很明顯嗎 -
type
開始規則更統一, 和var x int
和func Add(a, b int) int
類型後綴的規則是一致的(Rust中的變量和函數也是類型後置吧), 好比type MyInt int
,type MyFunc func(...)
, 並且也很是便於解析和查找(正則^type
就能夠定位了) -
若是要加
AssertEqual
的話, 那麼什麼叫equal
呢? 2個map或struct如何纔是叫相等, chan成員呢? 別老是想着增長功能, 增長功能的同時帶來的問題和複雜性難道不須要考慮嗎? -
語言太簡單難道不是優勢嗎? C++語言夠複雜, 建議樓主深刻學習
-
標準庫的一大原則就是基本可用, 標準庫不是一個大雜燴, 我想"少便是多"的哲學你是不會理解的
-
Go1.5默認N個系統線程, N爲CPU核心數目. 默認值並非沒有信心, 而是對於不一樣的程序, 須要幾個線程最好是一個比較困難的事情(好比gui程序爲什麼不用多線程呢).
-
oracle
就是一個普通的單詞, 爲什麼不能使用? 樓主會不會由於買了水果的手機, 之後就不認識apple
這個單詞了?
上面列出的是我目前還能想到的對Go語言的不爽之處,畢竟時間過去兩年多,還有一些早就遺忘了。其中一部分當然是小不爽,可能忍一忍就過去了,可是不少不爽積累起來,總會時不時地讓人難受,時間久了有自虐的感受。程序員的工做生活原本就夠枯燥的,何須呢。
必需要說的是,對於其中大多數不爽之處,我(Liigo)都曾經試圖改變過它們:在Go 1.0版本發佈以前,我在其官方郵件組提過不少意見和建議(甚至包括提交代碼CL),極力力排衆議,能夠說付出很大努力,目的就是但願定型後的Go語言是一個相對完善的、沒有明顯缺陷的編程語言。結果是使人失望的,我人微言輕、勢單力薄,不可能影響整個語言的發展走向。1.0以前,最佳的否認自我、超越自個人機會,就這麼遺憾地錯過了。我最終發現,不少時候不是技術問題,而是技術人員的問題。
給Go提交的CL的要求是很是高, 樓主的BOM提法我以爲能夠討論, 可是不要覺得CL增長了特性就必須得經過.
還好, Go團隊沒有接受你上面的諸多建議, 要否則我估計我如今已經放棄Go了.
第2節:我爲何對Go語言的某些人不爽?
這裏提到的「某些人」主要是兩類:1、負責專職開發Go語言的Google公司員工;2、Go語言的推崇者和腦殘粉絲。我跟這兩類人打過不少交道,不勝其煩。再次強調一遍,我指的是「某些」人,而不是全部人,請不要對號入座。
對於一, 執拗的G員工, 你要經過邏輯來講服他們, 若是本身都沒有乾貨, 別人憑什麼要採納你的建議(上面的絕大部分建議我就反對)?
對於二, 腦殘粉絲誰都煩, 但願樓主下次吐槽能給點乾貨, 別把本身也整成了腦殘粉.
Google公司內部負責專職開發Go語言的核心開發組某些成員,他們傾向於閉門造車,執拗己見,對第三方提出的建議不重視。他們經常掛在嘴邊的口頭禪是:現有的作法很好、不須要那個功能、咱們開發Go語言是給Google本身用的、Google不須要那個功能、若是你必定要改請fork以後本身改、別幹提意見請提交代碼。不少言行都是「反開源」的。經過一些具體的例子,還能更形象的看清這一層。就留下做爲課後做業吧。
對於技術而言, 我更喜歡獨裁者. 所謂的開源爛民主那是活稀泥的.
你能夠嘗試去提一個Linux內核也增長GUI模塊的建議試試.
我最不能接受的就是他們對1.0版本的散漫處理。那時候Go還沒到1.0,初出茅廬的小學生,有很大的改進空間,是全面翻新的最佳時機,彼時不改更待什麼時候?1.0是打地基的版本,基礎不牢靠,等1.0定型以後,到處受到向後兼容性的牽制,束手縛腳,每前進一步都阻力重重。急於發佈1.0,過早定型,留下諸多遺憾,彰顯了開發者的功利性強,在技術上不追求盡善盡美。
Go1.5的地基已經很是的牢固, 這個你不用擔憂.
Go語言的核心開發成員,他們平常的開發工做是使用C語言——Go語言的編譯器和運行時庫,包括語言核心數據結構和算法map、channel、scheduler,都是C開發的——真正用本身開發的Go語言進行實際的大型應用開發的機會並很少。雖然標準庫是用Go語言本身寫的,但他們卻沒有大範圍使用標準庫的經歷。實際上,他們缺乏使用Go語言的實戰開發經驗,每每不知道處於開發第一線的用戶真正須要什麼,沒法作到設身處地爲程序員着想。缺乏使用Go語言的親身經歷,也意味着他們不能在平常開發中,及時發現和改進Go語言的不足。這也是他們每每自我感受良好的緣由。
缺乏使用Go語言的親身經歷, 樓主也真的敢信口開河. 你不是覺得G公司開發Go真的是用來玩的吧.
再說Go1.5已經徹底沒有C代碼了, 這下你該閉口了吧.
Go語言社區裏,有一大批Go語言的推崇者和腦殘粉絲,他們知足於現狀,不思進取,到處維護心中的「神」,容不得批評意見,不支持對語言的改進要求。當年我對Go語言的不少批評和改進意見,極少獲得他們的支持,他們不但不支持還給予打擊,我就納悶了,他們難道不但願Go語言更完善、更優秀嗎?我後來才意識到,他們跟喬幫主的蘋果腦殘粉絲們,言行一脈相承,具備極端宗教傾向,神化主子、打擊異己真是竭盡全力呀。簡簡單單的技術問題,就能被他們上升到意識形態之爭。現實的例子是蠻多的,有興趣的到網上去找吧。正是由於他們的存在,致使更多理智、清醒的Go語言用戶沒法真正融入整個社區。
你的不少批評和改進意見都是狗屎(包括BOM那個). 你這樣的用戶沒有融入社區時好事情, Go語言只要在生產環境好用就能夠了.
若是一個項目、團隊、社區,處處充斥着讚美、孤芳自賞、自我知足、不思進取,排斥不一樣意見,拒絕接納新方案,我想不到它還有什麼前進的動力。逆水行舟,是不進反退的。
惋惜世界不是以你的意志改變的, Go還將繼續快速發展, 你是很難受吧?
第3節:還有比Go語言更好的選擇嗎?
我始終堅持一個很有辯證法意味的哲學觀點:在更好的替代品出現以前,現有的就是最好的。失望是沒有用的,抱怨是沒有用的,要麼接受,要麼逃離。我曾經努力嘗試過接受Go語言,失敗以後,註定要逃離。發現更好的替代品以後,無疑加速了逃離過程。還有比Go語言更好的替代品嗎?固然有。做爲一個屌絲程序員,我應該告訴你它是什麼,可是我不說。如今還不是時候。我如今不想把這兩門編程語言對立起來,引起另外一場潛在的語言戰爭。這不是此文的本意。若是你非要從現有信息中推測它是什麼,那徹底是你本身的事。若是你原意等,它或許很快會浮出水面,也未可知。
不就是號稱銀彈的 Rust 嗎, 可是 然並卵. 我也斷言一句: Rust 最終只能是小衆語言, 想代替 Go語言/C語言 根本是沒戲的(Swift開源後基本能夠秒殺Rust).
第4節:寫在最後
我不原意被別人表明,也不肯意表明別人。這篇文章寫的是我,一個叫Liigo的80後屌絲程序員,本身的觀點。你徹底能夠主觀地認爲它是主觀的,也徹底能夠客觀地覺得它是客觀的,不管如何,那是你的觀點。
這篇文字是從記憶裏收拾出來的。有些細節雖可考,而不值得考。——我早已逃離,不肯再回到當年的場景。文中涉及的某些細節,可能會由於些許誤差,影響其準確性;也可能會由於缺乏出處,影響其客觀性。若是有人較真,非要去核實,我相信那些東西應該還在那裏。
Go語言也非上文所述一無可取,它固然有它的優點和特點。讀者們判斷一件事物,應該是優劣並陳,作綜合分析,不能單聽我一家負面之言。可是它的那些不爽之處,始終讓我不爽,且不能從其優秀處得以徹底中和,這是我不得不放棄它的緣由。
走好, 不送!
Liigo 2014-4-29 補記1:
Go語言社區還有一個很奇特的現象,就是中國社區獨大,國外社區要小的多。有外國網友還專門寫了一篇文章研究《爲何Golang中國社區獨大》這個問題(文中也提到了我這篇博文)。一般來講,在IT和軟件領域,向來都是國外先進國家引領技術潮流,而後國內緩慢跟進。而到了Go語言這裏,偏偏反過來了,彷佛暗示着在國外的主流軟件開發技術人員並不怎麼待見Go語言,Go只是在國內受到一幫人的盲目推崇而已,至於這幫人的眼光如何,反正我不看好。
在工做中都已經用上了, 你還在想象別人是在盲目推崇, 是你本身在夢遊吧.
Liigo 2014-4-29 補記2:
著名的編程語言研究專家王垠寫了一篇《對 Go 語言的綜合評價》(晚於本博文發表約三五天),也是整體上持批判態度,看衰Go語言。讀者們能夠對照閱讀。
王的垠語言出來了嗎? 等着10年後他再次扇本身的臉(參考Windows無用那篇).
Liigo 2014-4-29 補記3:
Go語言的擁護者們,彷佛連Go語言的「核心優點」都說不出幾條。知乎上頗有人氣的一條問答《爲何要使用 Go 語言,Go 語言的優點在哪裏》,連靜態編譯、GC、跨平臺都拿出來講了(無視C/C++/Java),甚至連簡單易學(無視Python/易語言)、「豐富的」標準庫(跟誰比?敢跟Java/C#/Python比麼?)、好用的工具鏈(gofmt)都扯出來了,可見除了「併發、網絡」以外,他們也講不出另外的什麼核心優點了,只能靠一些周邊的東西湊數。
不須要NB的特性, 只須要簡單/好用/實用就行.
Liigo 2015-1-31 補記4:
全世界認爲Go語言很差的可不僅是我Liigo一我的。國外著名的問答網站Quora上面有我的氣很高的提問,「爲何不要用Go語言」(英文網頁),看看那排名最前的兩個答案,以及廣大程序員們給這兩個答案的數百個「贊」,都足以說明Go語言自身的問題是客觀存在的。人民羣衆的眼睛是雪亮的。
就是數萬個「贊」又怎麼樣? 關鍵是不少地方Go已經用起來了.
Liigo 2015-4-1 補記5:
文中1.10(黑魔法)和1.12(接口)章節增長了兩處「延伸閱讀」連接,被引用的連接後面均有大量網友評論。此舉主要是爲了說明本文觀點並不是一家之言。
Liigo 2015-5-29 補記6:
補充說明Go語言直到2015年下半年1.5發佈後纔將GOMAXPROCS設置爲大於1的默認值(HN),他們文中認可以前一直默認設置爲1是由於調度器不完善(與我此文最初發表時的猜想一致)。
原來這是你的功勞!
Liigo 2015-6-2 補記7:
補充兩篇英文:Why Go Is Not Good(做者Will Yager重點批評了Go語言的設計不佳甚至是倒退),Leaving Go(做者Danny Gratzer放棄Go語言的緣由主要是:沒有泛型,充滿黑魔法)。這兩篇文章都是針對具體問題作具體分析的,與本文寫做精神一致,務實不務虛。其中提到的對Go語言不滿的地方,本文也多有涉及,結論相似。
放棄Go語言很正常, 也有不少放棄X語言投奔Go語言的例子.
關於對做者傾向性質疑的聲明:
讀者看到本文全都是Go語言負面性的內容,沒有涉及一點Go語言好的地方,於是質疑做者的盲目傾向。出現這種結果徹底是由於文章主題所限。此前本文末尾也簡單提到過,評估一件事物,應當優劣並陳,優點項加分,劣勢項減分,作綜合評估分析。若是有突出的重大優點,則能夠容忍一些較大的劣勢;但若是有致命的劣勢或多項大劣勢,則再大的優點也沒法與之中和。中國乒乓球界講領軍人物必須作到「技術全面,特長突出,沒有明顯弱點」,我甚爲贊同。用這句話套用Go語言,能夠說「技術不全面(人家本身說成簡潔),有一點特長(併發),有明顯的弱點(包括但不限於本文列出的這些)」。如此一來,優點都被劣勢中和了,劣勢仍是那麼突出,天然是得負分,天然是棄用,天然是沒有好印象。我在這裏能夠說觀點鮮明、態度明確,不和稀泥。與其看那些盲目推崇Go語言的人和文章,籠統的說「好」,不如也順便看看本文,具體到細節地說「很差」。凡是具體到細節的東西,都是容易證明或證僞的,比籠統的東西(不管是"黑"仍是"粉")可信性更高一些。
不須要NB的特性, 只須要簡單/好用/實用就行.
關於對做者陰謀論的聲明:
有某些陰謀論者(例如謝某),說我因一個Pull Request被Go開發者拒絕而「懷恨至今」,暗示此文是故意報復、抹黑Go語言。我對Golang有恨嗎?固然是有的,那是一個不爽接一個不爽(如本文一一羅列的那些),逐步累積,由量變造成質變的結果,是我對Golang綜合客觀評估以後的主觀態度,並不是由哪個單獨的事件所主導。要說Pull Request被拒絕,Rust開發者拒絕個人PR次數還少嗎?好比 https://github.com/mozilla/rust/pull/13014 和 https://github.com/liigo/rust/tree/xp 和 https://github.com/rust-lang/rust/issues/12842,要是再算上被拒的Issues,那就多的數不清了。我顯然不可能由於某些個別的事件,影響到我對某個事物的綜合評估(參見前文)。那本文是「故意抹黑」Go語言嗎?我以爲不是,理由有二:一、這是做者的主觀感覺,二、這些感覺是以許多客觀事實爲基礎的。若是本文一一列出的那些現象,是不存在的,是虛構出來的,是憑空生成的,那麼做者必定是「低級黑」。問題是,那些都是客觀存在的事實。把事實說出來,怎麼能叫「黑」呢?歡迎讀者客觀而詳細的指正本文中的全部錯誤。
CL被據而懷恨至今真的是沒冤枉樓主, 但願下次抹黑Go能來點乾貨.
固然Go語言也不是完美的, 做爲Windows下的Go用戶, 說下我比較但願的改進.
首先在下面的bug修復前,我並不十分關心Go的性能改進。
- 修復 Issue11058, 讓 Go 能夠生成 dll, 這樣我就能夠基本拋棄 C++ 了
- 修復 Issue9510, 這樣 cgo 才能夠放心地靜態連接 c++ 庫
上面2個bug是支持go和c庫雙向合做的關鍵(Linux和Darwin已經支持生成動態庫). 而後就是 cgo 調用 c 函數的參數傳遞的性能能改善下.
在go1.5中, 新引入了 vendor 的試驗性的特性, 所以go的包依賴管理算是基本解決.
長遠看但願語言方面能有如下的特性:
- 範型支持, 能夠簡陋些, 可是不要破壞go已有的風格
- 但願能有不支持嵌套的三元表達式的支持
- 大小寫的導出規則對中文能友好一些
- 接口癮式轉換致使的一些坑(
error
和nil
) - 官方的leveldb庫和基於其封裝的sql數據庫
- os 的文件系統作成接口, 提共自定義文件系統的掛載功能
- image 包能增長 GrayA/GrayA32/RGB/RGB48 之類的類型支持
- 性能改進
無關緊要的:
- GUI支持
- IDE支持