【編者按】此篇文章轉(zhuǎn)載自Scott Huang的GitHub,以便更多語言愛好者學(xué)習(xí)和交流,尤其是C/C++和Rust,希望對各位有用。
以下為原文翻譯:
簡單講,原文http://eax.me/cpp-will-never-die/是俄語,有人感興趣,得到作者同意后,把它翻成英文。(譯者:然后我再把它翻成中文。)
顯而易見,這篇博文將會(huì)導(dǎo)致一場語言大圣戰(zhàn),所以,請思考兩遍,確定你將會(huì)通過“有建設(shè)性的辯論”的評論參與討論后再開始閱讀這篇文章。
再次說明原文是俄語:)
注意:進(jìn)一步講,我冒昧的認(rèn)為Rust有意嘗試創(chuàng)建一個(gè)快速并且安全的語言。畢竟,Mozilla的人最初構(gòu)思用它作為工具來開發(fā)一個(gè)瀏覽器引擎。如果它被證明是另外一個(gè)僅僅安全的語言,那么我認(rèn)為 它沒有達(dá)成目標(biāo)。那里有許多非常不同的安全語言供人們選擇和品味,如果Rust沒有打算代替C++,那么:
- 為什么它需要包含一個(gè)不安全子集;
- 并且,為什么作者要拋棄Rust的輕量級(jí)進(jìn)程?畢竟它們很方便,對吧?換句話說,如果我假設(shè)錯(cuò)了,那么整件事情就沒有討論的意義了。
如有你碰巧偶爾逛逛linux.org.ru論壇,那么請被提醒到這篇文章沒有觸及為什么不喜歡Rust的那10條純技術(shù)理由。一條和親愛的伙伴@sum3rman的Skype交談透露出不止一種的“技術(shù)性”理由的看法。所以,我不得不承認(rèn),我下面羅列的東西不討人喜歡,但是我還是冒險(xiǎn)從中引用一些最感興趣的條款到這里。實(shí)際上,一些普通共識(shí)的理由自己就足夠大到不用觸及技術(shù)性的討論。
對于每一個(gè)理智程序員都非常清楚的知道的C/C++在近期不會(huì)死掉。沒有人會(huì)嘗試重用新語言新編寫幾乎所有已經(jīng)存在的桌面應(yīng)用程序,操作系統(tǒng)內(nèi)核、編譯器、游戲以及瀏覽器引擎、虛擬機(jī)、數(shù)據(jù)庫、壓縮工具、音視頻編碼解碼器、一堆其他的C庫等等。這是一批數(shù)量巨大的快速的、調(diào)試過的被時(shí)間證明了的代碼。重寫的代價(jià)太昂貴了和太冒險(xiǎn)了,并且,誠實(shí)的講,除了一些瘋狂的Rust粉絲,沒人會(huì)認(rèn)為這有意義。對C/C++程序員的需求從來都是高的,并且在未來很長一段時(shí)間都是。
那么用Rust寫一些新的代碼怎么樣?
嗯,你也許記得,這并不是第一次嘗試創(chuàng)建一個(gè)“更好的”C/C++。拿D語言來舉例。它在2001年發(fā)布,且確實(shí)是一個(gè)好的語言。但沒有空間發(fā)展,沒有合適的開發(fā)工具,沒有著名的成功案例可以聯(lián)想到它。OpenMW項(xiàng)目最初用D開發(fā),但作者突然決定用C++從頭重寫。據(jù)他們坦白,他們收到一大堆的郵件說,“你們創(chuàng)建了一個(gè)很酷的項(xiàng)目,我想貢獻(xiàn)一些力量給它,但是我們不懂,也不喜歡學(xué)習(xí)這個(gè)愚蠢的D語言”。維基百科告訴我們,除了D,還有有其它嘗試準(zhǔn)備殺死C++ - 舉例說Vala、Cyclone、Limbo、Bitc。有多少人曾經(jīng)聽說過這些語言?
我覺得人們必須理解從歷史中得到教訓(xùn)。沒有一個(gè)理智的人會(huì)在他們的項(xiàng)目中開始使用一個(gè)新語言,直到你展示一些非常酷的開發(fā)支持工具,告訴他們一些成功故事,并且證明一堆程序員靠這個(gè)語言做日常工作維持生活。作為程序員,他們從來不會(huì) - 除了一些最年輕的人 - 花他們的時(shí)間和健康來學(xué)習(xí)另外一種“非常棒”的語言,直到你展示一些非常酷的開發(fā)工具(不是未完工的像Racer工具那樣)和許多確實(shí)準(zhǔn)備好的庫(不是“實(shí)驗(yàn)性的”或者“不穩(wěn)定的”東西),告訴他們一些成功案例,告訴他們有許多空缺在他們的城市或鄉(xiāng)鎮(zhèn)。你知道,這就像“雞和蛋”的兩難處境。只有非常少的機(jī)會(huì),這個(gè)問題確實(shí)得到解決(最近相關(guān)的案例是Go和Scala) - 這得感謝一些大公司(Google、Typesafe)的時(shí)間和金錢投入,他們基于某種理由認(rèn)為值得推廣一個(gè)新語言。
正如我提到的,有許多非技術(shù)性的理由單獨(dú)就可以質(zhì)疑Rust。但是,讓我們假想一會(huì)兒這些理由都不存在。那么沒有理由不用Rust寫程序,對吧?好的,這一點(diǎn)仍然非常可疑,這么說吧。
C/C++被從很多方面批判。順便說一下,大多數(shù)批評者還從來沒有看過產(chǎn)品級(jí)的C++代碼。簡短的說,C++的問題是非常快(并且只需求一點(diǎn)點(diǎn)內(nèi)存、電量,等等),但是從允許數(shù)組越界,自由的存取內(nèi)存等方面看不夠安全。過去,這個(gè)問題促使程序員們開發(fā)出一系列安全的語言,比如Java、C#、Python還有其他等等。但是,他們被證明和C++相比,對資源需求太多,同時(shí)還有其他一些不足 - 比如,比如當(dāng)進(jìn)行垃圾回收時(shí)“世界停止了”的問題。這也是為什么程序員爭扎地去創(chuàng)建一個(gè)和C++一樣快,但安全的語言。Rust是其中一個(gè)候選人。
Rust確實(shí)是安全的,但是,不幸的是,離快還差很遠(yuǎn)。在寫這篇文章的時(shí)候,它和Java,go和Haskell的性能如下圖所示:
我真誠的希望程序員找到一個(gè)方法來及時(shí)的加速,但直到那時(shí),幾乎沒有語言比Scala或者Go對安全/速度進(jìn)行妥協(xié)更加感興趣。是否可以使一種語言同時(shí)具有速度和安全,或者是否由于對數(shù)組越界,安全包裹C語言庫,或則其他一些類似東西而天生注定比C/C++慢兩倍的問題仍然懸而未決。
順便問一下,什么實(shí)際上使得Rust安全?簡單的說,這門語言有內(nèi)建的代碼分析器,非常艱難的一條:它可以捕獲全部典型的C++錯(cuò)誤,不僅處理內(nèi)存管理,而且同時(shí)考慮多線程。通過一條管道來傳遞一個(gè)指定對象的引用到另外一個(gè)線程,并且接著由你自己嘗試使用這個(gè)引用 - 程序會(huì)拒絕通過編譯。這確實(shí)非常酷。
但C++在過去30年一直屹立不倒,有大量的靜態(tài)的和動(dòng)態(tài)的分析器在這些時(shí)間段被發(fā)布出來。舉一個(gè)例子,看一個(gè)關(guān)于Google sanitizers(明智分析器)的一個(gè)短片 - 他們確實(shí)非常難。不管怎么說,在任何一個(gè)嚴(yán)肅的項(xiàng)目中,你使用一個(gè)不斷集成的系統(tǒng),運(yùn)行一大堆的測試來編譯程序。如果你不這么做的話,那么你的麻煩比語言缺乏安全性而言會(huì)更糟糕,因?yàn)殪o態(tài)的類型不會(huì)保證程序按你的邏輯正確的運(yùn)行!所以,由于你總是運(yùn)行測試,為什么不同時(shí)使用。另一方面,如果你在你的代碼的某個(gè)深處地方?jīng)]有檢查數(shù)組越界,并且Sanitizer也沒有報(bào)告這個(gè)錯(cuò)誤,也許,這僅僅由于所有必須的檢查已經(jīng)在上一層提供過了,同時(shí),多一次檢查不是會(huì)使程序變慢?即使沒有sanitizers,你還可以發(fā)現(xiàn)許多東西供你在不同平臺(tái)編譯項(xiàng)目時(shí)提供伴隨適當(dāng)?shù)氖д娴?assert(obj -> isvalid)"風(fēng)格論斷來檢查你代碼的不變性。粗糙的說,這個(gè)問題實(shí)際上源自過去那些好的圣戰(zhàn),關(guān)于異教徒和加爾各答接近軟件開發(fā)(指的是,一項(xiàng)創(chuàng)新太理想化的嘗試和一個(gè)傳統(tǒng)經(jīng)驗(yàn)主義者認(rèn)為被前者的支持者無心簡單化了 - 原譯者注)。
你經(jīng)常可以聽到一個(gè)爭論說,90%的執(zhí)行時(shí)間被10%的代碼所執(zhí)行(據(jù)我理解,僅僅是一條經(jīng)驗(yàn)主義 - 對一個(gè)主題快速的掃描互聯(lián)網(wǎng)無法替代任何嚴(yán)格的科學(xué)的研究)。因此,你可以用安全的Rust語言寫你大部分的代碼,然后,剩下的10%(那些“熱”代碼)寫在不安全的子集中,所以現(xiàn)有的Rust實(shí)現(xiàn)實(shí)際上不會(huì)有不好的性能。好的,但這不更是暗示我根本不需要Rust,因?yàn)槲铱梢杂肎o寫90%代碼,然后用C寫剩下的10%?只有那些尋找銀彈的人和幻想神話的異教徒會(huì)因?yàn)樗写a都100%用同一種語言編寫而感到開心,而用Rust。但實(shí)際上一種語言有兩者方言,這和"Java + C"或者"Go+C"組合沒什么不同。
但實(shí)際上90/10規(guī)則是垃圾話。按照它的邏輯,我們可以用Java90%重寫Webkit或者VirtualBox或者GCC而得到同樣的結(jié)果。但這顯然是錯(cuò)的。并不是這個(gè)比率因不同程序而變動(dòng)太大,讓我們做一些計(jì)算來瞧瞧。假設(shè)整個(gè)程序用不安全的C/C++編寫,并且它的執(zhí)行時(shí)間,假設(shè)是0.91(一小部分熱代碼)+0.11(大量的冷代碼)=1。現(xiàn)在和一個(gè)用一個(gè)帶有C代碼插入的安全語言編寫的程序做比較: 0.91 + 0.12 = 1.1,這樣,理論上說,區(qū)別是10%。是多了還是少了?這取決于項(xiàng)目的規(guī)模。以Google為例,即使是很少的一點(diǎn)比例都可以節(jié)省大量的金錢(請看第五小結(jié),“利用”,在這篇文章中)。或者想象當(dāng)下一次更新,JVM突然開始要求多10%的資源!我都害怕猜想需要多少個(gè)0在數(shù)字后面才能把比率轉(zhuǎn)化為金錢。10%是C和C++完成任務(wù)所需要的所有份額。
我們不停的念咒語“不成熟的優(yōu)化是所有邪惡的根源”。但如果我們想逐字的跟隨,為什么在所有代碼里不用冒泡排序來替換快速排序?畢竟,我們沒有辦法確切的知道哪里是瓶頸,對嗎?為什么要把普通的活動(dòng)包含在actors或者事務(wù)內(nèi)存中而不用馬上用更加有效率的原子?并且,通常說,在小案例中,強(qiáng)制性的初始化每一個(gè)單獨(dú)的變量,執(zhí)行一堆輔助性的檢查等等根本沒有意義。讓你僅花額外的幾分鐘去考慮而獲取即使只有2~5%而不是10%的性能改進(jìn),其實(shí)也不壞。此外,就像我們已經(jīng)指出的,這會(huì)使C/C++程序有巨大的不同!畢竟,誰敢爭辯說找到了熱點(diǎn),重寫那些代碼(也許有一堆)并且證明這真的變快了是一項(xiàng)比預(yù)先考慮性能而言更輕松的工作?
即使除了速度/安全問題比較之外,我還懷疑那語言的設(shè)計(jì)。特別對于它使用5種指針類型。一方面,讓程序員仔細(xì)考慮他們的變量存放在棧或堆里,允不允許被多線程操作的想法并不差。另一方面,想象你在寫一個(gè)程序,且在某一刻發(fā)現(xiàn)一些變量應(yīng)該存在堆里而不是棧上。所以你用Box重寫代碼。接著你發(fā)現(xiàn)你實(shí)際上需要Rc或者Arc。另外,你重寫了所有的代碼。接著,再次,你再次重寫它在棧上擁有普通變量。所有的東西你都手工操作而沒有使用一個(gè)合適的IDE。正則表達(dá)式?jīng)]有幫助。或者你剛剛結(jié)束一個(gè)噩夢像“Vec>>>” - 對Java說hello吧!但悲傷的事情是編譯器已經(jīng)知道每一個(gè)變量的每一件關(guān)于使用期的事,并會(huì)自動(dòng)插入所有這些Box's、Arc's等等。但由于某種原因,這個(gè)責(zé)任被轉(zhuǎn)移給了程序員。讓程序員簡單的寫val(我們活在第三個(gè)千年,畢竟!)會(huì)更加方便。并且顯式的在需要的地方指定特殊的Box或者Rc。從這一點(diǎn)看,Rust的開發(fā)人員搞砸了整件事情。這個(gè),特別的,讓Rust's的使用范圍更加窄了。沒有一個(gè)理智的人會(huì)用這樣的一個(gè)語言寫web或者服務(wù)器端軟件 - 特別當(dāng)考慮到它并沒有提供比JVM相關(guān)語言更顯著的優(yōu)勢。即使是Go - 帶有普通輕量級(jí)的進(jìn)程(不是未來) - 看起來都是一種更好的選擇來解決這些任務(wù)。至于未來,你得學(xué)會(huì)如何正確的操作它們而不會(huì)砸到自己的腳 - 并且你談到“安全”的語言,啊?確實(shí),所有的這些語言都有他們自己的獨(dú)特的怪癖 - 舉“整個(gè)世界都停止了”的例子。但這個(gè)問題可以通過把代碼分解成小的服務(wù)或者通過其它技術(shù)而解決。并且是的,沒有人愿意把Rust轉(zhuǎn)為Javascript,通過它來為AWS寫腳本或則為MongoDB來做查詢語言。至于安卓,它也不容易可信,但是有一個(gè)不同的理由:不僅僅只有一個(gè)架構(gòu)風(fēng)格,所以JVM可以做的更好。所以,如果你恰巧認(rèn)為Rust是“適合所有任務(wù)”的話,我讓你失望了。
還有更多的理由來終結(jié)它:
宏使用一個(gè)拐杖來彌補(bǔ)由于缺乏普通異常處理而導(dǎo)致的過度冗長。我已經(jīng)寫了關(guān)于元編程的問題 - 就是因?yàn)樗麄兪翘貏e的,導(dǎo)致我們沒有辦法得到一個(gè)合適的Rust IDE。并且,即使我并不確定,看起來Rust宏甚至并沒有命名空間。Cargo積極的鼓勵(lì)繞過Crates.io從git中直接下載各種倉庫就當(dāng)人們是白癡。作為一種結(jié)果,我們有被被巨量的混亂的包終結(jié)的風(fēng)險(xiǎn),就像Erlang世界中的Rabar。順便,我懷疑Go世界有同樣的麻煩。就像許多新語言,Rust在走簡單化的路。我通常理解為什么它沒有合適的繼承和例外,但事實(shí)本身是有些人替我做決定讓我覺得有些不舒服。C++不會(huì)限制程序員說哪些他們可以或者不可以做。現(xiàn)在,由于我們在走簡單化的路,為什么不拋棄所有那些語言擴(kuò)展?目前組成Haskell世界的那些東西是每個(gè)程序員用他們自己方言碼出來的。智能指針,讓你知道,遠(yuǎn)不會(huì)沒有代價(jià),也并不會(huì)確保一個(gè)固定時(shí)間的垃圾收集。假如一些線程榮幸的釋放一個(gè)非常深的數(shù)據(jù)結(jié)構(gòu)會(huì)發(fā)生什么?當(dāng)死引用在一個(gè)迷宮里流浪時(shí),所有依賴它的其他線程都安靜的耐心的等待著。Erlang以及它的一小片有同樣的困難 - 我曾經(jīng)自己面對過它許多次。智能指針自己本身也有一些問題 - 例如內(nèi)存碎片化和泄漏。就像讓一個(gè)弱指針在一個(gè)循環(huán)結(jié)構(gòu)里 - 整件事情搞砸了。所有這些都是一個(gè)語言試圖假裝變得安全點(diǎn)...如果你需要一個(gè)固定的GC時(shí)間,學(xué)習(xí)你程序的行為,減輕負(fù)載并且采取預(yù)防性(舉例,提供對象池)如果你不滿意這些數(shù)字,或者可以手工管理內(nèi)存。有人看到Rust里面嚴(yán)格的語義描述嗎?它至少有一個(gè)內(nèi)存模型嗎?當(dāng)你考慮到它可以用10種方法翻譯源代碼,你還會(huì)叫它為一個(gè)“安全”的語言可以寫出“確保正確”的程序?我不能,但再一次提醒你麻煩的根源通常是人,而不是技術(shù)。如果你的C++代碼沒有足夠好,或者Java代碼很痛苦的運(yùn)行緩慢,這不是由于這個(gè)技術(shù)不好 - 這是因?yàn)槟氵沒有學(xué)會(huì)如何正確的使用它。因此,你也不會(huì)因?yàn)槠渌恍├碛蓪ust滿意。學(xué)習(xí)那些更流行的工具并且喜歡上它不是更容易嗎?所以,總結(jié)一下,個(gè)人來說,在下一個(gè)五年左右,我會(huì)投資我的時(shí)間去學(xué)習(xí)C/C++而不是Rust。C++是一個(gè)工業(yè)標(biāo)準(zhǔn)。程序員們已經(jīng)習(xí)慣用它去解決巨量的差異化的任務(wù)超過30年了。至于Rust和其他類似的 - 他們僅僅是奇怪的玩具帶有模糊的優(yōu)點(diǎn)。從2000年開始人們已經(jīng)假設(shè)C++很快就會(huì)死掉,但自從那時(shí)開始起,C/C++并沒有變得少用。相反的,事實(shí)上,它演進(jìn)了(C++11、C++14),新的工具發(fā)布了(舉例說Clion和Clang),并且空間巨大。
一個(gè)C++程序員從來不會(huì)很難找到一個(gè)工資不錯(cuò)的工作,必要的話,可以很快的學(xué)會(huì)Rust。但相反的情況非常非常不會(huì)發(fā)生。順便說一下,找工作時(shí),語言的選擇從來都不是唯一和最重要事。另外,一個(gè)熟練的C/C++程序員可以容易的學(xué)習(xí)PostgreSQL's或者Linux核心代碼,接觸現(xiàn)代的強(qiáng)大的開發(fā)工具,并且有一大堆的書和文章在手(比如說OpenGL)。
所以,關(guān)心一下你的健康吧,不要浪費(fèi)你的時(shí)間 - 你只有比你想象的更少的時(shí)間!