<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Go 10 個與眾不同的特性

          共 2559字,需瀏覽 6分鐘

           ·

          2022-02-15 09:35

          Go 作為一門相對較新的語言,能夠脫穎而出,肯定是多方面的原因。本文聊聊它不同于其他語言的 10 個特性。


          Go 的創(chuàng)建者 Robert Griesemer[1]Rob Pike[2]Ken Thompson[3] 在 Google 工作,在那里,大規(guī)模擴展的挑戰(zhàn)激發(fā)了他們將 Go 設計為具有大型代碼庫的項目的快速高效的編程解決方案,由多個開發(fā)人員管理,具有嚴格的性能要求,并跨越多個網(wǎng)絡和處理核心。

          Go 的創(chuàng)始人在創(chuàng)建新語言時也抓住了這個機會,從其他編程語言的優(yōu)勢,劣勢和疏忽中學習。結(jié)果是一種干凈,清晰和實用的語言,具有相對較小的命令和特性集。

          本文將介紹 Go 的 10 個特性,這些特性(根據(jù)我個人的觀察)將其與其他語言區(qū)分開來。

          1. Go 始終在構(gòu)建中包含 runtime

          Go 運行時提供內(nèi)存分配、垃圾回收、并發(fā)支持和網(wǎng)絡等服務。它被編譯進每個 Go 二進制文件。這與許多其他語言不同,其中許多語言使用虛擬機,需要與程序一起安裝才能正常工作。

          將運行時直接包含在二進制文件中使得分發(fā)和運行 Go 程序變得非常容易,并避免了運行時與程序之間的不兼容問題。Python,Ruby 和 JavaScript 等語言的虛擬機也沒有針對垃圾回收和內(nèi)存分配進行優(yōu)化,這解釋了 Go 相對于其他類似語言的優(yōu)越速度。例如,Go 盡可能多地存儲在堆棧[4]上,其中數(shù)據(jù)按順序排列,以便比[5]更快地訪問。稍后將對此進行詳細介紹。

          關(guān)于 Go 的靜態(tài)二進制文件的最后一件事是,由于不需要運行外部依賴項,因此它們的啟動速度非常快。如果你使用像 Google App Engine[6] 這樣的服務,這將非常有用,這是一種在 Google Cloud 上運行的平臺即服務,可以將你的應用程序擴展到零實例以節(jié)省云成本。當有新的請求出現(xiàn)時,App Engine 可以在眨眼間啟動你的 Go 程序?qū)嵗T?Python 或 Node 中相同的體驗通常會導致 3-5 秒的等待(或更長時間),因為所需的虛擬環(huán)境也與新實例一起旋轉(zhuǎn)。

          2. Go 沒有集中托管的程序依賴服務

          為了訪問已發(fā)布的 Go 程序,開發(fā)人員不依賴于集中托管的服務,例如用于 Java 的Maven Central[7]或用于 JavaScript 的NPM[8]。相反,項目通過其源代碼存儲庫(通常是 GitHub)共享。go get/install 命令行允許以這種方式下載存儲庫。

          為什么我喜歡這個功能?我一直認為集中托管的依賴服務(如 Maven Central、PIP 和 NPM)有著令人生畏的黑匣子,可能會抽象出下載和安裝依賴項(以及依賴項的依賴項)的麻煩,但當依賴項錯誤發(fā)生時,不可避免地會引發(fā)可怕的心跳加速(我經(jīng)歷過太多了,無法計數(shù))。

          很多時候,我發(fā)現(xiàn)令人沮喪的是,我從來沒有完全理解它們內(nèi)部是如何工作的。通過取消中央服務,安裝,版本控制和管理 Go 項目的依賴項的過程非常清晰,從而更加清晰。(當然,也有人喜歡集中托管)

          此外,將模塊提供給其他人就像將其放入版本控制系統(tǒng)中一樣簡單,這是分發(fā)程序的一種非常簡單的方法。

          3. Go 是按值調(diào)用

          在 Go 中,當你提供基本類型(數(shù)字、布爾值或字符串)或結(jié)構(gòu)(類對象的大致等效項)作為函數(shù)的參數(shù)時,Go 始終會創(chuàng)建變量值的副本

          在許多其他語言如 Java,Python 和 JavaScript 中,基本類型是通過值傳遞[9]的,但是對象(類實例)是通過引用傳遞的,這意味著接收函數(shù)實際上接收到指向原始對象的指針而不是其副本

          這意味著在接收函數(shù)中對對象所做的任何更改都將反映在原始對象中

          在 Go 中,結(jié)構(gòu)和基本類型默認按值傳遞,可以選擇通過使用星號運算符傳遞指針[10]

          //?pass?by?value
          func?MakeNewFoo(f?Foo)?(Foo,?error)?{
          ???f.Field1?=?"New?val"
          ???f.Field2?=?f.Field2?+?1
          ???return?f,?nil
          }

          上述函數(shù)接收 Foo 的副本,并返回一個新的 Foo 對象

          //?pass?by?reference
          func?MutateFoo(f?*Foo)?error?{
          ???f.Field1?=?"New?val"
          ???f.Field2?=?2
          ???return?nil
          }

          上面的函數(shù)接收指向 Foo 的指針并改變原始對象

          這種按值調(diào)用與按引用調(diào)用的明顯區(qū)別使你的意圖顯而易見,并減少了調(diào)用函數(shù)無意中改變傳入對象的可能性(這是許多初學者開發(fā)人員難以掌握的)。

          正如麻省理工學院總結(jié)[11]的那樣:"可變性使得理解你的程序在做什么變得更加困難,而執(zhí)行合約也更難"

          更重要的是,按值調(diào)用可顯著減少垃圾回收器的工作,這意味著更快、更節(jié)省內(nèi)存的應用程序。這篇文章[12]得出的結(jié)論是,指針追蹤(從堆中檢索指針值)比從連續(xù)堆棧中檢索值慢 10 到 20 倍。要記住的一個很好的經(jīng)驗法則是:從內(nèi)存中讀取的最快方法是按順序讀取它,這意味著將隨機存儲在 RAM 中的指針數(shù)量減少到最低限度

          4. defer 關(guān)鍵字

          在 NodeJS 中,在我開始使用knex.js[13]之前,我會在代碼中手動管理數(shù)據(jù)庫連接,方法是創(chuàng)建一個數(shù)據(jù)庫池,然后在每個函數(shù)的池中打開一個新連接,一旦所需的數(shù)據(jù)庫 CRUD 功能完成,就會在函數(shù)結(jié)束時釋放連接。

          這有點像維護的噩夢,因為如果我在每個函數(shù)結(jié)束時不釋放連接,未釋放的數(shù)據(jù)庫連接的數(shù)量將慢慢增長,直到池中沒有更多的可用連接,然后中斷應用程序。

          現(xiàn)實情況是,程序通常必須發(fā)布,清理和執(zhí)行資源,文件,連接等,因此 Go 引入了defer關(guān)鍵字作為管理這一點的有效方法。

          任何前面帶有defer的語句都會延遲其調(diào)用,直到周圍的函數(shù)退出。這意味著你可以將清理/拆卸代碼放在函數(shù)的頂部(很明顯),知道一旦函數(shù)完成,它就會完成它的工作。

          func?main()?{
          ????if?len(os.Args)?2?{
          ????????log.Fatal("no?file?specified")
          ????}
          ????f,?err?:=?os.Open(os.Args[1])
          ????if?err?!=?nil?{
          ????????log.Fatal(err)
          ????}
          ????defer?f.Close()
          ????data?:=?make([]byte,?2048)
          ????for?{
          ????????count,?err?:=?f.Read(data)
          ????????os.Stdout.Write(data[:count])
          ????????if?err?!=?nil?{
          ????????????if?err?!=?io.EOF?{
          ????????????????log.Fatal(err)
          ????????????}
          ????????????break
          ????????}
          ????}
          }

          在上面的示例中,文件關(guān)閉方法被延遲。我喜歡這種模式,在函數(shù)的頂部聲明你的內(nèi)務管理意圖,然后忘記它,知道一旦函數(shù)退出,它就會完成它的工作。

          5. Go 吸納了函數(shù)式編程的最佳特性

          函數(shù)式編程是一種高效且富有創(chuàng)造性的范式,值得慶幸的是,Go 采納了函數(shù)式編程的最佳特性。在 Go 中:

          — 函數(shù)是值,這意味著它們可以作為值添加到 map 中,作為參數(shù)傳遞到其他函數(shù)中,設置為變量,并從函數(shù)返回(稱為"高階函數(shù)",在 Go 中經(jīng)常用于使用裝飾器模式創(chuàng)建中間件)。

          — 匿名函數(shù)可以創(chuàng)建并自動調(diào)用。

          — 在其他函數(shù)中聲明的函數(shù)允許閉包(其中在函數(shù)內(nèi)部聲明的函數(shù)能夠訪問和修改在外部函數(shù)中聲明的變量)。在慣用的 Go 中,閉包被廣泛使用,限制了函數(shù)的作用域,并設置了函數(shù)在其邏輯中使用的狀態(tài)。

          func?StartTimer?(name?string)?func(){
          ????t?:=?time.Now()
          ????log.Println(name,?"started")
          ????return?func()?{
          ????????d?:=?time.Now().Sub(t)
          ????????log.Println(name,?"took",?d)
          ????}
          }
          func?RunTimer()?{
          ????stop?:=?StartTimer("My?timer")
          ????defer?stop()
          ????time.Sleep(1?*?time.Second)
          }

          以上是閉包的一個例子。'StartTimer' 函數(shù)返回一個新函數(shù),該函數(shù)通過閉包可以訪問在其啟動作用域中設置的 't' 值。然后,此函數(shù)可以將當前時間與 "t" 的值進行比較,從而創(chuàng)建一個有用的計時器。感謝Mat Ryer[14]的這個例子。

          6. Go 有隱式接口實現(xiàn)

          任何讀過SOLID[15]編碼和設計模式[16]文獻的人都可能聽說過 "偏愛組合而不是繼承" 的口頭禪。簡而言之,這表明你應該將業(yè)務邏輯分解為不同的接口,而不是依賴于父類中屬性和邏輯的分層繼承。

          另一個流行的方法是 "面向接口編程,而不是實現(xiàn)":API 應該只發(fā)布其預期行為的契約(其方法簽名),但不能詳細介紹如何實現(xiàn)該行為。

          這兩者都指出了接口在現(xiàn)代編程中的至關(guān)重要性。

          因此,毫不奇怪,Go 支持接口。事實上,接口是 Go 中唯一的抽象類型。

          然而,與其他語言不同,Go 中的接口不是顯式實現(xiàn)的,而是隱式實現(xiàn)的。具體類型不聲明它實現(xiàn)接口。相反,如果該具體類型的方法集包含基礎接口的所有方法集,則 Go 認為該對象實現(xiàn)了該接口

          這種隱式接口實現(xiàn)(正式名稱為結(jié)構(gòu)化類型 structural typing)允許 Go 強制實施類型安全和解耦,從而保留了動態(tài)語言中表現(xiàn)出的大部分靈活性。

          相比之下,顯式接口將客戶端和實現(xiàn)綁定在一起,例如,在 Java 中替換依賴項比在 Go 中困難得多。

          //?this?is?an?interface?declaration?(called?Logic)
          type?Logic?interface?{
          ????Process(data?string)?string
          }

          type?LogicProvider?struct?{}
          //?this?is?a?method?called?'Process'?on?the?LogicProvider?struct
          func?(lp?LogicProvider)?Process(data?string)?string?{
          ????//?business?logic
          }
          //?this?is?the?client?struct?with?the?Logic?interface?as?a?property
          type?Client?struct?{
          ????L?Logic
          }
          func(c?Client)?Program()?{
          ????//?get?data?from?somewhere
          ????c.L.Process(data)
          }
          func?main()?{
          ????c?:=?Client?{
          ????????L:?LogicProvider{},
          ????}
          ????c.Program()
          }

          LogicProvider 中沒有任何聲明表明它實現(xiàn)了 Logic 接口。這意味著客戶端將來可以輕松替換其邏輯提供程序,只要該邏輯提供程序包含基礎接口 (Logic) 的所有方法集。

          7. 錯誤處理

          Go 中的錯誤處理方式與其他語言大不相同。簡而言之,Go 通過返回 error 類型的值作為函數(shù)的最后一個返回值來處理錯誤

          當函數(shù)按預期執(zhí)行時,將為 error 參數(shù)返回 nil,否則返回錯誤值。然后,調(diào)用函數(shù)檢查錯誤返回值,并處理錯誤,或引發(fā)自己的錯誤。

          //?the?function?returns?an?int?and?an?error
          func?calculateRemainder(numerator?int,?denominator?int)?(int,?error)?{
          ???//?Error?returned
          ???if?denominator?==?0?{
          ??????return?9,?errors.New("denominator?is?0")
          ???}
          ???//?No?error?returned
          ???return?numerator?/?denominator,?nil
          }

          Go 以這種方式運行是有原因的:它迫使編碼人員考慮異常并正確處理它們。傳統(tǒng)的 try-catch 異常還會在代碼中添加至少一個新的代碼路徑,并以難以遵循的方式縮進代碼。Go 更喜歡將"快樂路徑"視為非縮進代碼,在"快樂路徑"完成之前識別并返回任何錯誤。

          8. 并發(fā)

          并發(fā)可以說是 Go 最著名的功能,并發(fā)允許在機器或服務器上的可用內(nèi)核數(shù)量上并行運行任務。當單獨的進程不相互依賴(不需要按順序運行)并且時間性能至關(guān)重要時,并發(fā)性最有意義。I/O 要求通常就是這種情況,其中讀取或?qū)懭氪疟P或網(wǎng)絡比除最復雜的內(nèi)存中進程之外的所有進程慢幾個數(shù)量級。

          函數(shù)調(diào)用之前的 'go' 關(guān)鍵字將開啟并發(fā) goroutine 運行該函數(shù)。

          func?process(val?int)?int?{
          ???//?do?something?with?val
          }
          //?for?each?value?in?'in',?run?the?process?function?concurrently,
          //?and?read?the?result?of?process?to?'out'
          func?runConcurrently(in?<-chan?int,?out?chan<-?int){
          ???go?func()?{
          ???????for?val?:=?range?in?{
          ????????????result?:=?process(val)
          ????????????out?<-?result
          ???????}
          ???}
          }

          Go 中的并發(fā)性是一項深入且相當高級的功能,但在有意義的情況下,它提供了一種有效的方法來確保程序的最佳性能。

          9. Go 標準庫

          Go 具有"電池包含"的理念,現(xiàn)代編程語言的許多需求都融入了標準庫中,這使得程序員的生活變得更加簡單。

          如前所述,Go 是一種相對年輕的語言,這意味著標準庫中滿足了現(xiàn)代應用程序的許多問題/需求。

          首先,Go 為網(wǎng)絡(特別是 HTTP/2)和文件管理提供了世界一流的支持。它還提供本地 JSON 編碼和解碼。因此,設置服務器來處理 HTTP 請求和返回響應(JSON 或其他)非常簡單,這解釋了 Go 在開發(fā)基于 REST 的 HTTP Web 服務方面的受歡迎程度。

          正如Mat Ryer[17]還指出的那樣,標準庫是開源的,是學習 Go 最佳實踐的絕佳方式。

          10. 調(diào)試:Go Playground

          使用任何語言進行調(diào)試都是一項關(guān)鍵需求。大多數(shù)語言都依賴于第三方在線工具或聰明的 IDE 來提供調(diào)試工具,使開發(fā)人員能夠快速檢查其代碼。Go 提供了 Go Playground — https://go.dev/play 一個免費的在線工具,你可以在其中試用和共享小程序。這是一個非常有用的工具,使調(diào)試成為一項簡單的練習。

          沒記錯的話,Go 應該開啟了 playground 的先河,之后發(fā)布的語言也提供類似的功能,比如 Rust 和 Swift。

          總結(jié)

          除了以上介紹的 10 個特性,你認為還有其他特性是 Go 獨特的地方嗎?

          參考資料

          [1]

          Robert Griesemer: https://en.wikipedia.org/wiki/Robert_Griesemer

          [2]

          Rob Pike: https://en.wikipedia.org/wiki/Rob_Pike

          [3]

          Ken Thompson: https://en.wikipedia.org/wiki/Ken_Thompson

          [4]

          堆棧: https://en.wikipedia.org/wiki/Stack-based_memory_allocation

          [5]

          堆: https://www.educba.com/what-is-heap-memory/

          [6]

          Google App Engine: https://cloud.google.com/appengine

          [7]

          Maven Central: https://search.maven.org/

          [8]

          NPM: https://www.npmjs.com/

          [9]

          是通過值傳遞: https://itnext.io/the-power-of-functional-programming-in-javascript-cc9797a42b60

          [10]

          指針: https://www.ardanlabs.com/blog/2017/05/language-mechanics-on-stacks-and-pointers.html

          [11]

          總結(jié): http://web.mit.edu/6.031/www/fa20/classes/08-immutability/

          [12]

          這篇文章: https://www.forrestthewoods.com/blog/memory-bandwidth-napkin-math/

          [13]

          knex.js: https://knexjs.org/

          [14]

          Mat Ryer: https://twitter.com/matryer

          [15]

          SOLID: https://en.wikipedia.org/wiki/SOLID

          [16]

          設計模式: https://en.wikipedia.org/wiki/Software_design_pattern

          [17]

          Mat Ryer: https://twitter.com/matryer



          往期推薦


          我是 polarisxu,北大碩士畢業(yè),曾在 360 等知名互聯(lián)網(wǎng)公司工作,10多年技術(shù)研發(fā)與架構(gòu)經(jīng)驗!2012 年接觸 Go 語言并創(chuàng)建了 Go 語言中文網(wǎng)!著有《Go語言編程之旅》、開源圖書《Go語言標準庫》等。


          堅持輸出技術(shù)(包括 Go、Rust 等技術(shù))、職場心得和創(chuàng)業(yè)感悟!歡迎關(guān)注「polarisxu」一起成長!也歡迎加我微信好友交流:gopherstudio


          瀏覽 38
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产一级α片 | 伊人网站在线观看 | 亚洲毛多水多 | 黄色。****yor | 无码人妻精品一区二区三千菊电影 |