Go2 Transition
Go2的設(shè)計草案在Go 2 Draft Designs或者這里暂幼,而Go1如何遷移到Go2也是我個人特別關(guān)心的問題俐载,Python2和Python3的那種不兼容的遷移方式簡直就是噩夢一樣的記憶盟广。Go的提案中欺劳,有一個專門說了遷移的問題厌均,參考Go2 Transition梅肤。
Go2 Transition還不是最終方案身坐,不過它也對比了各種語言的遷移,還是很有意思的一個總結(jié)蛋叼。這個提案描述了在非兼容性變更時焊傅,如何給開發(fā)者挖的坑最小剂陡。
目前Go1的標(biāo)準(zhǔn)庫是遵守兼容性原則的,參考Go 1 compatibility guarantee狐胎,這個規(guī)范保證了Go1沒有兼容性問題鸭栖,幾乎可以沒有影響的升級比如從Go1.2升級到Go1.11。幾乎
的意思握巢,是很大概率是沒有問題晕鹊,當(dāng)然如果用了一些非常冷門的特性,可能會有坑暴浦,我們遇到過json解析時溅话,內(nèi)嵌結(jié)構(gòu)體的數(shù)據(jù)成員也得是exposed的才行,而這個在老版本中是可以非exposed歌焦;還遇到過cgo對于鏈接參數(shù)的變更導(dǎo)致編譯失敗飞几,這些問題幾乎很難遇到,都可以算是兼容的吧独撇,有時候只是把模糊不清的定義清楚了而已屑墨。
Go2在語言和標(biāo)準(zhǔn)庫上,會打破Go1的兼容性規(guī)范纷铣,也就是和Go1不再兼容绪钥。不過Go是分布式開源社區(qū)在維護(hù),不能依賴于flag day关炼,還是要容許不同Go版本寫的package的互操作性程腹。先了解下各個語言如何考慮兼容性:
-
C是嚴(yán)格向后兼容的,很早寫的程序總是能在新的編譯器中編譯儒拂。另外新的編譯器也支持指定之前的標(biāo)準(zhǔn)寸潦,比如
-std=c90
使用ISO C90
標(biāo)準(zhǔn)編譯程序。關(guān)鍵的特性是編譯成目標(biāo)文件后社痛,不同版本的C的目標(biāo)文件见转,能完美的鏈接成執(zhí)行程序。C90實際上是對之前K&R C
版本不兼容的蒜哀,主要引入了volatile
關(guān)鍵字斩箫,還有整數(shù)精度問題,還引入了trigraphs撵儿,最糟糕的是引入了undefined行為比如數(shù)組越界和整數(shù)溢出的行為未定義乘客。從C上可以學(xué)到的是:后向兼容非常重要;非常小的打破兼容性也問題不大特別是可以通過編譯器選項來處理淀歇;能將不同版本的目標(biāo)文件鏈接到一起是非常關(guān)鍵的易核;undefined行為嚴(yán)重困擾開發(fā)者容易造成問題。 - C++也是ISO組織驅(qū)動的語言浪默,和C一樣也是向后兼容的牡直。C++和C一樣坑爹的地方坑到吐血缀匕,比如undefined行為等等。盡管一直保持向后兼容碰逸,但是新的C++代碼比如C++11看起來完全不同乡小,這是因為有新的改變的特性,比如很少會用裸指針饵史,比如range代替了傳統(tǒng)的for循環(huán)劲件,這導(dǎo)致熟悉老C++語法的程序員看新的代碼非常難受甚至看不懂。C++毋庸置疑是非常流行的约急,但是新的語言標(biāo)準(zhǔn)在這方面沒有貢獻(xiàn)。從C++上可以學(xué)到的新東西是:盡管保持向后兼容苗分,語言的新版本可能也會帶來巨大的不同的感受(保持向后兼容并不能保證能持續(xù)看懂)厌蔽。
- Java也是向后兼容的,是在字節(jié)碼層面和語言層面都向后兼容摔癣,盡管語言上不斷的新增了關(guān)鍵字奴饮。Java的標(biāo)準(zhǔn)庫非常龐大,也不斷的在更新择浊,過時的特性會被標(biāo)記為deprecated并且編譯時會有警告戴卜,理論上一定版本后deprecated的特性會不可用。Java的兼容性問題主要在JVM解決琢岩,如果用新的版本編譯的字節(jié)碼投剥,得用新的JVM才能執(zhí)行。Java還做了一些前向兼容担孔,這個影響了字節(jié)碼啥的(我本身不懂Java江锨,作者也不說自己不是專家,我就沒仔細(xì)看了)糕篇。Java上可以學(xué)到的新東西是:要警惕因為保持兼容性而限制語言未來的改變啄育。
-
Python2.7是2010年發(fā)布的,目前主要是用這個版本拌消。Python3是2006年開始開發(fā)挑豌,2008年發(fā)布,十年后的今天還沒有遷移完成墩崩,甚至主要是用的Python2而不是Python3氓英,這當(dāng)然不是Go2要走的路○谐铮看起來是因為缺乏向后兼容導(dǎo)致的問題债蓝,Python3刻意的和之前版本不兼容,比如print從語句變成了一個函數(shù)盛龄,string也變成了Unicode(這導(dǎo)致和C調(diào)用時會有很多問題)饰迹。沒有向后兼容芳誓,同時還是解釋型語言,這導(dǎo)致Python2和3的代碼混著用是不可能的啊鸭,這以為著程序依賴的所有庫必須支持兩個版本锹淌。Python支持
from __future__ import FEATURE
,這樣可以在Python2中用Python3的特性赠制。Python上可以學(xué)到的東西是:向后兼容是生死攸關(guān)的赂摆;和其他語言互操作的接口兼容是非常重要的;能否升級到新的語言是由調(diào)用的庫支持的钟些。 - Perl6是2000年開始開發(fā)的烟号,15年后才正式發(fā)布,這也不是Go2應(yīng)該走的路政恍。這么漫長的主要原因包括汪拥,刻意沒有向后兼容,只有語言的規(guī)范沒有實現(xiàn)而這些規(guī)范不斷的修改篙耗。Perl上可以學(xué)到的東西是:不要學(xué)Perl迫筑;設(shè)置期限按期交付;別一下子全部改了宗弯。
特別說明的是脯燃,非常高興的是Go2不會重新走Python3的老路子,當(dāng)初被Python的版本兼容問題坑得不要不要的蒙保。
雖然上面只是列舉了各種語言的演進(jìn)辕棚,確實也了解得更多了,有時候描述問題本身邓厕,反而更能明白解決方案坟募。C和C++的向后兼容確實非常關(guān)鍵,但也不是他們能有今天地位的原因邑狸,C++11的新特性到底增加了多少DAU呢懈糯,確實是值得思考的。另外C++11加了那么多新的語言特性单雾,比如WebRTC代碼就是這樣赚哗,很多老C++程序員看到后一臉懵逼,和一門新的語言一樣了硅堆,是否保持完全的兼容不能做一點點變更屿储,其實也不是的。
應(yīng)該將Go的語言版本和標(biāo)準(zhǔn)庫的版本分開考慮渐逃,這兩個也是分別演進(jìn)的够掠,例如alias是1.9引入的向后兼容的特性,1.9之前的版本不支持茄菊,1.9之后的都支持疯潭。語言方面包括:
- Language additions新增的特性赊堪,比如1.9新增的type alias。這些向后兼容的新特性竖哩,并不要求代碼中指定特殊的版本號哭廉,比如用了alias的代碼不用指定要1.9才能編譯,用之前的版本會報錯相叁。向后兼容的語言新增的特性遵绰,是依靠程序員而不是工具鏈來維護(hù)的,要用這個特性或庫升級到要求的版本就可以增淹。
-
Language removals刪除的特性椿访。比如有個提案#3939去掉
string(int)
,字符串構(gòu)造函數(shù)不支持整數(shù)虑润,假設(shè)這個在Go1.20版本去掉成玫,那么Go1.20之后這種string(1000)
代碼就要編譯失敗了。這種情況沒有特別好的辦法能解決端辱,我們可以提供工具,將代碼自動替換成新的方式虽画,這樣就算庫維護(hù)者不更新舞蔽,使用者自己也能更新。這種場景引出了指定最大版本码撰,類似C的-std=C90
渗柿,可以指定最大編譯的版本比如-lang=go1.19
荆姆,當(dāng)然必須能和Go1.20的代碼鏈接必盖。指定最大版本可以在go.mod中指定,這需要工具鏈兼容歷史的版本剔宪,由于這種特性的刪除不會很頻繁柴梆,維護(hù)負(fù)擔(dān)還是可以接受的陨溅。 -
Minimum language version最小要求版本。為了可以更明確的錯誤信息绍在,可以允許模塊在
go.mod
中指定最小要求的版本门扇,這不是強制性的,只是說明了這個信息后編譯工具能明確的給出錯誤偿渡,比如給出應(yīng)該用具體哪個版本臼寄。 - Language redefinitions語言重定義。比如Go1.1時溜宽,int在64位系統(tǒng)中長度從4字節(jié)變成了8字節(jié)吉拳,這會導(dǎo)致很多潛在的問題。比如#20733修改了變量在for中的作用域适揉,看起來是解決潛在的問題留攒,但也可能會引入問題煤惩。引入關(guān)鍵字一般不會有問題,不過如果和函數(shù)沖突就會有問題稼跳,比如error: check盟庞。為了讓Go的生態(tài)能遷移到Go2,語言重定義的事情應(yīng)該盡量少做汤善,因為我們不再能依賴編譯器檢查錯誤什猖。雖然指定版本能解決這種問題,但是這始終會導(dǎo)致未知的結(jié)果红淡,很有可能一升級Go版本就掛了不狮。我覺得對于語言重定義,應(yīng)該完全禁止在旱。比如#20733可以改成禁止這種做法摇零,這樣就會變成編譯錯誤,可能會幫助找到代碼中潛在的BUG桶蝎。
- Build tags編譯tags驻仅。在指定文件中指定編譯選項,是現(xiàn)有的機制登渣,不過是指定的release版本號噪服,它更多是指定了最小要求的版本,而沒有解決最大依賴版本問題胜茧。
-
Import go2導(dǎo)入新特性粘优。和Python的特性一樣,可以在Go1中導(dǎo)入Go2的新特性呻顽,比如可以顯示的導(dǎo)入
import "go2/type-aliases"
雹顺,而不是在go.mod中隱式的指定。這會導(dǎo)致語言比較復(fù)雜廊遍,將語言打亂成了各種特性的組合嬉愧。而且這種方式一旦使用,將無法去掉喉前。這種方式看起來不太適合Go英染。
如果有更多的資源來維護(hù)和測試,標(biāo)準(zhǔn)庫后續(xù)會更快發(fā)布被饿,雖然還是6個月的周期四康。標(biāo)準(zhǔn)庫方面的變更包括:
-
Core standard library核心標(biāo)準(zhǔn)庫。有些和編譯工具鏈相關(guān)的庫狭握,還有其他的一些關(guān)鍵的庫闪金,應(yīng)該遵守6個月的發(fā)布周期,而且這些核心標(biāo)準(zhǔn)庫應(yīng)該保持Go1的兼容性,比如
os/signal
哎垦、reflect
囱嫩、runtime
、sync
漏设、testing
墨闲、time
、unsafe
等等郑口。我可能樂觀的估計net
,os
, 和syscall
不在這個范疇鸳碧。 -
Penumbra standard library邊緣標(biāo)準(zhǔn)庫。它們被獨立維護(hù)犬性,但是在一個release中一起發(fā)布瞻离,當(dāng)前核心庫大部分都屬于這種。這使得可以用
go get
等工具來更新這些庫乒裆,比6個月的周期會更快套利。標(biāo)準(zhǔn)庫會保持和前面版本的編譯兼容,至少和前面一個版本兼容鹤耍。 -
Removing packages from the standard library去掉一些不太常用的標(biāo)準(zhǔn)庫肉迫,比如
net/http/cgi
等。
如果上述的工作做得很好的話稿黄,開發(fā)者會感覺不到有個大版本叫做Go2喊衫,或者這種緩慢而自然的變化逐漸全部更新成了Go2。甚至我們都不用宣傳有個Go2抛猖,既然沒有C2.0為何要Go2.0呢格侯?主流的語言比如C鼻听、C++和Java從來沒有2.0财著,一直都是1.N的版本,我們也可以模仿他們撑碴。事實上撑教,一般所認(rèn)為的全新的2.0版本,若出現(xiàn)不兼容性的語言和標(biāo)準(zhǔn)庫醉拓,對用戶也不是個好結(jié)果伟姐,甚至還是有害的。
Links
由于簡書限制了文章字?jǐn)?shù)亿卤,只好分成不同章節(jié):
- Overview 為何Go有時候也叫Golang?為何要選擇Go作為服務(wù)器開發(fā)的語言愤兵?是沖動?還是騷動排吴?Go的重要里程碑和事件秆乳,當(dāng)年吹的那些牛逼,都實現(xiàn)了哪些?
- Could Not Recover 君可知屹堰,有什么panic是無法recover的肛冶?包括超過系統(tǒng)線程限制,以及map的競爭寫扯键。當(dāng)然一般都能recover睦袖,比如Slice越界、nil指針荣刑、除零馅笙、寫關(guān)閉的chan等。
- Errors 為什么Go2的草稿3個有2個是關(guān)于錯誤處理的嘶摊?好的錯誤處理應(yīng)該怎么做延蟹?錯誤和異常機制的差別是什么?錯誤處理和日志如何配合叶堆?
- Logger 為什么標(biāo)準(zhǔn)庫的Logger是完全不夠用的阱飘?怎么做日志切割和輪轉(zhuǎn)?怎么在混成一坨的服務(wù)器日志中找到某個連接的日志虱颗?甚至連接中的流的日志沥匈?怎么做到簡潔又夠用?
- Interfaces 什么是面向?qū)ο蟮腟OLID原則忘渔?為何Go更符合SOLID高帖?為何接口組合比繼承多態(tài)更具有正交性?Go類型系統(tǒng)如何做到looser, organic, decoupled, independent, and therefore scalable畦粮?一般軟件中如果出現(xiàn)數(shù)學(xué)散址,要么真的牛逼要么裝逼。正交性這個數(shù)學(xué)概念在Go中頻繁出現(xiàn)宣赔,是神仙還是妖怪预麸?為何接口設(shè)計要考慮正交性?
- Modules 如何避免依賴地獄(Dependency Hell)儒将?小小的版本號為何會帶來大災(zāi)難吏祸?Go為什么推出了GOPATH、Vendor還要搞module和vgo钩蚊?新建了16個倉庫做測試贡翘,碰到了9個坑,搞清楚了gopath和vendor如何遷移砰逻,以及vgo with vendor如何使用(畢竟生產(chǎn)環(huán)境不能每次都去外網(wǎng)下載)鸣驱。
- Concurrency & Control 服務(wù)器中的并發(fā)處理難在哪里?為什么說Go并發(fā)處理優(yōu)勢占領(lǐng)了云計算開發(fā)語言市場蝠咆?什么是C10K踊东、C10M問題?如何管理goroutine的取消、超時和關(guān)聯(lián)取消递胧?為何Go1.7專門將context放到了標(biāo)準(zhǔn)庫碑韵?context如何使用,以及問題在哪里缎脾?
- Engineering Go在工程化上的優(yōu)勢是什么祝闻?為什么說Go是一門面向工程的語言?覆蓋率要到多少比較合適遗菠?什么叫代碼可測性联喘?為什么良好的庫必須先寫Example?
- Go2 Transition Go2會像Python3不兼容Python2那樣作嗎辙纬?C和C++的語言演進(jìn)可以有什么不同的收獲豁遭?Go2怎么思考語言升級的問題?
- SRS & Others Go在流媒體服務(wù)器中的使用贺拣。Go的GC靠譜嗎蓖谢?Twitter說相當(dāng)?shù)目孔V,有圖有真相譬涡。為何Go的聲明語法是那樣闪幽?C的又是怎樣?是拍的大腿涡匀,還是拍的腦袋盯腌?