關於GO語言,這篇文章講的很明白


摘要:本文從Go的語法,類型系統,編碼風格,語言工具,編碼工具和使用案例等幾方面對Go語言進行了學習和探討。

Go語言發布之后,很多公司特別是雲廠商也開始用Go語言重構產品的基礎架構,而且很多企業都是直接采用Go語言進行開發,最近熱火朝天的Docker就是采用Go語言進行開發的。本文我們一起來探討和學習一下Go語言的技術特點。先來看個例子:

package main
 
import (
    "fmt"
    "time"
)
 
// 要在goroutine中運行的函數。done通道將被用來通知工作已經完成。
func worker(done chan bool) {
    fmt.Print("working...")
    time.Sleep(time.Second)
    fmt.Println("done")
    // 通知完成。
    done <- true
}
 
func main() {
 
    // 創建一個通道
    done := make(chan bool, 1)
    go worker(done)
 
    // 等待done變為true
    <-done
}

上例中是一個在Go語言中使用goroutine和通道的例子。 其中:

 go 關鍵字是用來啟動一個goroutine

 done <- true, 向通道傳值

 <-done, 讀取通道值

一、【概述】

Go是由RobertGriesemer、RobPike和KenThompson在Google設計的一種靜態類型化的、須編譯后才能運行的編程語言。

Go在語法上類似於C語言,但它具有C語言沒有的優勢,如內存安全、垃圾回收、結構化的類型和CSP風格的並發性。

它的域名是,所以通常被稱為"Golang",但正確的名稱是Go。

二、【GO的設計思想】

Go的設計受C語言的影響,但更加簡單和安全。該語言包括如下特點:

  • 采用動態語言中比較常見的語法和環境模式:
  • 可選的簡明變量聲明和通過類型推理進行初始化(如果使用x := 0而不是int x= 0;或var x= 0;)。
  • 快速編譯。
  • 遠程包管理(go get)和在線包文檔。
  • 針對特定問題的獨特方法:
  • 內置的並發基元:輕量級處理機制(goroutines)、通道和select語句。
  • 用接口系統代替虛擬繼承,用類型嵌入代替非虛擬繼承。
  • 默認情況下,由一個工具鏈生成靜態鏈接的原生二進制文件,沒有外部依賴關系。
  • 希望保持語言規范足夠簡單,程序員容易掌握。

2.1 簡潔的語法

Go的語法包含C語言中保持代碼簡潔性和可讀性的語法特點。

2.1.1 變量聲明

引入了一個聯合聲明/初始化操作符,允許程序員寫出i := 3或s :="Hello, world!",而不需要指定使用的變量類型。

這與C語言中的int i= 3; 和 const char *s = "Hello, world!";形成鮮明對比。

2.1.2 分號隱含

分號仍然是終止語句,但在行結束時是隱含的。

2.1.3 返回多值

在Go中,一個函數方法可以返回多個值,返回一個結果和錯誤err組合對是向調用者提示錯誤的常規方式。

2.1.4 范圍表達式

Go的范圍表達式允許在數組、動態數組、字符串、字典和通道上進行簡潔的迭代,在C語言中,有三種循環來實現這個功能。

2.2 類型系統

2.2.1 內置的類型

Go有許多內置的類型,包括數字類型(byte、int64、float32等)、booleans和字符串(string)。

字符串是不可更改的。

內置的運算符和關鍵字(而不是函數)提供了串聯、比較和UTF-8編碼/解碼。

2.2.2 結構類型

記錄類型可以用struct關鍵字定義。

2.2.3 數組類型

對於每個類型T和每個非負整數常數n,都有一個數組類型,表示為[n]T,因此,不同長度的數組有不同的類型。

動態數組可以作為"Slice"使用,如對於某類型T,表示為[]T。這些數組有一個長度和一個容量,容量規定了何時需要分配新的內存來擴展數組。若干個Slice可以共享它們的底層內存。

2.2.4 指針

所有類型都可以定義指針, T類型的指針可定義為*T。地址抽取和隱式訪問使用&和*操作符,這跟C語言一樣,或者隱式的通過方法調用或屬性訪問使用。

除了標准庫中的特殊的unsafe.Pointer類型,一般指針沒有指針運算。

2.2.5 映射類型

對於一個組合對類型K、V,類型map[K]V是將類型K鍵映射到類型V值的哈希表的類型。

2.2.6 通道類型

chan T是一個通道,允許在並發的Go進程之間發送T類型的值。

2.2.7 顯式類型

除了對接口的支持外,Go的類型系統是顯示的:類型關鍵字可以用來定義一個新的命名類型,它與其他具有相同布局的命名類型(對於結構體來說,相同的成員按相同的順序排列)不同。類型之間的一些轉換(如各種整數類型之間的轉換)是預先定義好的,添加一個新的類型可以定義額外的轉換,但命名類型之間的轉換必須始終顯式調用。例如,類型關鍵字可以用來定義IPv4地址的類型,基於32位無符號整數:

type ipv4addr uint32

通過這個類型定義,ipv4addr(x)將uint32值x解釋為IP地址。如果簡單地將x分配給類型為ipv4addr的變量將會是一個類型錯誤。

常量表達式既可以是類型化的,也可以是 "非類型化的";如果它們所代表的值通過了編譯時的檢查,那么當它們被分配給一個類型化的變量時,就會被賦予一個類型。

2.2.8 函數類型

函數類型由func關鍵字表示;它們取0個或更多的參數並返回0個或更多的值,這些值都是要聲明類型的。

參數和返回值決定了一個函數的類型;比如,func(string, int32)(int, error)就是輸入一個字符串和一個32位有符號的整數,並返回一個有符號的整數和一個錯誤(內置接口類型)的值的函數類型。

2.2.9 類型上的方法擴展

任何命名的類型都有一個與之相關聯的方法集合。上面的IP地址例子可以用一個檢查其值是否為已知標准的方法來擴展:

// ZeroBroadcast報告addr是否為255.255.255.255.255。
func (addr ipv4addr) ZeroBroadcast() bool {
    return addr == 0xFFFFFFFF
}

以上的函數在ipv4addr上增加了一個方法,但這個方法在uint32上沒有。

2.2.10 接口系統

Go提供了兩個功能來取代類繼承。

首先是嵌入方法,可以看成是一種自動化的構成形式或委托代理。

第二種是接口,它提供了運行時的多態性。

接口是一類型,它在Go的類型系統中提供了一種有限的結構類型化形式。

一個接口類型的對象同時也有另一種類型的定義對應,這點就像C++對象同時具有基類和派生類的特征一樣。

Go接口是在Smalltalk編程語言的協議基礎上設計的。

在描述Go接口時使用了鴨式填充這個術語。

雖然鴨式填充這個術語沒有精確的定義,它通常是說這些對象的類型一致性沒有被靜態檢查。

由於Go接口的一致性是由Go編譯器靜態地檢查的,所以Go的作者們更喜歡使用結構類型化這個詞。

接口類型的定義按名稱和類型列出了所需的方法。任何存在與接口類型I的所需方法匹配的函數的T類型的對象也是類型I的對象。類型T的定義不需要也不能識別類型I。例如,如果Shape、Square和Circle被定義為:

import "math"
 
type Shape interface {
    Area() float64
}
 
type Square struct { // 注:沒有 "實現 "聲明
    side float64
}
 
func (sq Square) Area() float64 { return sq.side * sq.side }
 
type Circle struct { // 這里也沒有 "實現 "聲明
    radius float64
}
 
func (c Circle) Area() float64 { return math.Pi * math.Pow(c.radius, 2) }
 
 

一個正方形和一個圓都隱含着一個形狀(Shape)類型,並且可以被分配給一個形狀(Shape)類型的變量。

Go的接口系統使用了了結構類型。接口也可以嵌入其他接口,其效果是創建一個組合接口,而這個組合接口正是由實現嵌入接口的類型和新定義的接口所增加的方法來滿足的。

Go標准庫在多個地方使用接口來提供通用性,這包括基於Reader和Writer概念的輸入輸出系統。

除了通過接口調用方法,Go還允許通過運行時類型檢查將接口值轉換為其他類型。這就是類型斷言和類型切換。

空接口{}是一個重要的基本情況,因為它可以引用任何類型的選項。它類似於Java或C#中的Object類,可以滿足任何類型,包括像int這樣的內置類型。

使用空接口的代碼不能簡單地在被引用的對象上調用方法或內置操作符,但它可以存儲interface{}值,通過類型斷言或類型切換嘗試將其轉換為更有用的類型,或者用Go的reflect包來檢查它。

因為 interface{} 可以引用任何值,所以它是一種擺脫靜態類型化限制的有效方式,就像C 語言中的 void*,但在運行時會有額外的類型檢查。

接口值是使用指向數據的指針和第二個指向運行時類型信息的指針來實現的。與Go中其他一些使用指針實現的類型一樣,如果未初始化,接口值是零。

2.3 程序包系統

在Go的包系統中,每個包都有一個路徑(如"compress/bzip2 "或"")和一個名稱(如bzip2或html)。

對其他包的定義的引用必須始終以其他包的名稱作為前綴,並且只有其他包的大寫的名稱才能被訪問:io.Reader是公開的,但bzip2.reader不是。

go get命令可以檢索存儲在遠程資源庫中的包,鼓勵開發者在開發包時,在與源資源庫相對應的基礎路徑

(如http://example.com/user_name/package_name)內開發程序包,從而減少將來在標准庫或其他外部庫中名稱碰撞的可能性。

有人提議Go引入一個合適的包管理解決方案,類似於CPANfor Perl或Rust的Cargo系統或Node的npm系統。

2.4 並發:goroutines和通道

2.4.1 【CSP並發模式】

在計算機科學中,通信順序過程(communicating sequential processes,CSP)是一種描述並發系統中交互模式的正式語言,它是並發數學理論家族中的一個成員,被稱為過程算法(process algebras),或者說過程計算(process calculate),是基於消息的通道傳遞的數學理論。

CSP在設計Oceam編程語言時起了很大的影響,同時也影響了Limbo、RaftLib、Go、Crystal和Clojure的core.async等編程語言的設計。

CSP最早是由TonyHoare在1978年的一篇論文中描述的,后來有了很大的發展。

CSP作為一種工具被實際應用於工業上,用於指定和驗證各種不同系統的並發功能,如T9000Transputer以及安全的電子商務系統。

CSP本身的理論目前也仍然是被積極研究的對象,包括增加其實際適用范圍的工作,如增加可分析的系統規模。

Go語言有內置的機制和庫支持來編寫並發程序。並發不僅指的是CPU的並行性,還指的是異步性處理:讓相對慢的操作,如數據庫或網絡讀取等操作在做其他工作的同時運行,這在基於事件的服務器中很常見。

主要的並發構造是goroutine,這是一種輕量級處理類型。一個以go關鍵字為前綴的函數調用會在一個新的goroutine中啟動這個函數。

語言規范並沒有指定如何實現goroutine,但目前的實現將Go進程的goroutine復用到一個較小的操作系統線程集上,類似於Erlang中的調度。

雖然一個標准的庫包具有大多數經典的並發控制結構(mutex鎖等),但Go並發程序更偏重於通道,它提供了goroutines之間的消息傳功能。

可選的緩沖區以FIFO順序存儲消息,允許發送的goroutines在收到消息之前繼續進行。

通道是類型化的,所以chan T類型的通道只能用於傳輸T類型的消息。

特殊語法約定用於對它們進行操作;<-ch是一個表達式,它使執行中的goroutine在通道ch上阻塞,直到有一個值進來,而ch<- x則是發送值x(可能阻塞直到另一個goroutine接收到這個值)。

內置的類似於開關的選擇語句可以用來實現多通道上的非阻塞通信。Go有一個內存模型,描述了goroutine必須如何使用通道或其他操作來安全地共享數據。

通道的存在使Go有別於像Erlang這樣的actor模型式的並發語言,在這種語言中,消息是直接面向actor(對應於goroutine)的。在Go中,可以通過在goroutine和通道之間保持一對一的對應關系來,Go語言也允許多個goroutine共享一個通道,或者一個goroutine在多個通道上發送和接收消息。

通過這些功能,人們可以構建像workerpools、流水線(比如說,在下載文件時,對文件進行解壓縮和解析)、帶超時的后台調用、對一組服務的"扇出"並行調用等並發構造。

通道也有一些超越進程間通信的常規概念的用途,比如作為一個並發安全的回收緩沖區列表,實現coroutines和實現迭代器。

Go的並發相關的結構約定(通道和替代通道輸入)來自於TonyHoare的通信順序進程模型。

不像以前的並發編程語言,如Occam或Limbo(Go的共同設計者RobPike曾在此基礎上工作過的語言),Go沒有提供任何內置的安全或可驗證的並發概念。

雖然在Go中,上述的通信處理模型是推薦使用的,但不是唯一的:一個程序中的所有goroutines共享一個單一的地址空間。這意味着可突變對象和指針可以在goroutines之間共享。

2.5 並行編程的舒適度

有一項研究比較了一個不熟悉Go語言的老練程序員編寫的程序的大小(以代碼行數為單位)和速度,以及一個Go專家(來自Google開發團隊)對這些程序的修正,對Chapel、Cilk和IntelTBB做了同樣的研究。

研究發現,非專家傾向於用每個遞歸中的一條Go語句來寫分解-解決算法,而專家則用每個處理器的一條Go語句來寫分布式工作同步程序。Go專家的程序通常更快,但也更長。

2.6 條件競賽安全問題

Goroutine對於如何訪問共享數據沒有限制,這使得條件競賽成為可能的問題。

具體來說,除非程序通過通道或其他方式顯式同步,否則多個goroutine共享讀寫一個內存區域可能會發生問題。

此外,Go的內部數據結構,如接口值、動態數組頭、哈希表和字符串頭等內部數據結構也不能幸免於條件競賽,因此在多線程程序中,如果修改這些類型的共享實例沒有同步,就會存在影響類型和內存安全的情況。

2.7 二進制生成

gc工具鏈中的鏈接器默認會創建靜態鏈接的二進制文件,因此所有的Go二進制文件都包括Go運行所需要的內容。

2.8 舍棄的語言特征

Go故意省略了其他語言中常見的一些功能,包括繼承、通用編程、斷言、指針運算、隱式類型轉換、無標記的聯合和標記聯合。

2.9 Go風格特點

Go作者在Go程序的風格方面付出了大量的努力:

  • gofmt工具自動規范了代碼的縮進、間距和其他表面級的細節。
  • 與Go一起分發的工具和庫推薦了一些標准的方法,比如API文檔(godoc)、 測試(go test)、構建(go build)、包管理(go get)等等。
  • Go的一些規則跟其他語言不同,例如禁止循環依賴、未使用的變量或導入、隱式類型轉換等。
  • 某些特性的省略(例如,函數編程的一些捷徑,如map和Java風格的try/finally塊)編程風格顯式化,具體化,簡單化。
  • Go團隊從第一天開始就發布了一個Go的語法使用集合,后來還收集了一些代碼的評論,講座和官方博客文章,來推廣Go的風格和編碼理念。

三、【Go的工具】

主要的Go發行版包括構建、測試和分析代碼的工具。

 go build,它只使用源文件中的信息來構建Go二進制文件,不使用單獨的makefiles。

 gotest,用於單元測試和微基准

 go fmt,用於格式化代碼

 go get,用於檢索和安裝遠程包。

 go vet,靜態分析器,查找代碼中的潛在錯誤。

 go run,構建和執行代碼的快捷方式

 godoc,用於顯示文檔或通過HTTP

 gorename,用於以類型安全的方式重命名變量、函數等。

 go generate,一個標准的調用代碼生成器的方法。

它還包括分析和調試支持、運行時診斷(例如,跟蹤垃圾收集暫停)和條件競賽測試器。

第三方工具的生態系統增強了標准的發布系統,如:

gocode,它可以在許多文本編輯器中自動完成代碼,

goimports(由Go團隊成員提供),它可以根據需要自動添加/刪除包導入,以及errcheck,它可以檢測可能無意中被忽略的錯誤代碼。

四、【編輯環境】

流行的Go代碼工具:

 GoLand:JetBrains公司的IDE。

 VisualStudio Code

 LiteIDE:一個"簡單、開源、跨平台的GoIDE"

 Vim:用戶可以安裝插件:

 vim-go

五、【應用案例】

用Go編寫的一些著名的開源應用包括:

 Caddy,一個開源的HTTP/2web服務器,具有自動HTTPS功能。

 CockroachDB,一個開源的、可生存的、強一致性、可擴展的SQL數據庫。

 Docker,一套用於部署Linux容器的工具。

 Ethereum,以太幣虛擬機區塊鏈的Go-Ethereum實現。

 Hugo,一個靜態網站生成器

 InfluxDB,一個專門用於處理高可用性和高性能要求的時間序列數據的開源數據庫。

 InterPlanetaryFile System,一個可內容尋址、點對點的超媒體協議。

 Juju,由UbuntuLinux的包裝商Canonical公司推出的服務協調工具。

 Kubernetes容器管理系統

 lnd,比特幣閃電網絡的實現。

 Mattermost,一個團隊聊天系統

 NATSMessaging,是一個開源的消息傳遞系統,其核心設計原則是性能、可擴展性和易用性。

 OpenShift,雲計算服務平台

 Snappy,一個由Canonical開發的UbuntuTouch軟件包管理器。

 Syncthing,一個開源的文件同步客戶端/服務器應用程序。

 Terraform,是HashiCorp公司的一款開源的多雲基礎設施配置工具。

其他使用Go的知名公司和網站包括:

 Cacoo,使用Go和gRPC渲染用戶儀表板頁面和微服務。

 Chango,程序化廣告公司,在其實時競價系統中使用Go。

 CloudFoundry,平台即服務系統

 Cloudflare,三角編碼代理Railgun,分布式DNS服務,以及密碼學、日志、流處理和訪問SPDY網站的工具。

 容器Linux(原CoreOS),是一個基於Linux的操作系統,使用Docker容器和rkt容器。

 Couchbase、Couchbase服務器內的查詢和索引服務。

 Dropbox,將部分關鍵組件從Python遷移到了Go。

 谷歌,許多項目,特別是下載服務器

 Heroku,Doozer,一個提供鎖具服務的公司

 HyperledgerFabric,一個開源的企業級分布式分類賬項目。

 MongoDB,管理MongoDB實例的工具。

 Netflix的服務器架構的兩個部分。

 Nutanix,用於其企業雲操作系統中的各種微服務。

 Plug.dj,一個互動式在線社交音樂流媒體網站。

 SendGrid是一家位於科羅拉多州博爾德市的事務性電子郵件發送和管理服務。

 SoundCloud,"幾十個系統"

 Splice,其在線音樂協作平台的整個后端(API和解析器)。

 ThoughtWorks,持續傳遞和即時信息的工具和應用(CoyIM)。

 Twitch,他們基於IRC的聊天系統(從Python移植過來的)。

 Uber,處理大量基於地理信息的查詢。

六、【代碼示例】

6.1 Hello World

package main
 
import "fmt"
 
func main() {
    fmt.Println("Hello, world!")
}
 

6.2 並發

package main
 
import (
    "fmt"
    "time"
)
 
func readword(ch chan string) {
    fmt.Println("Type a word, then hit Enter.")
    var word string
    fmt.Scanf("%s", &word)
    ch <- word
}
 
func timeout(t chan bool) {
    time.Sleep(5 * time.Second)
    t <- false
}
 
func main() {
    t := make(chan bool)
    go timeout(t)
 
    ch := make(chan string)
    go readword(ch)
 
    select {
    case word := <-ch:
        fmt.Println("Received", word)
    case <-t:
        fmt.Println("Timeout.")
    }
}

6.3 代碼測試

沒有測試的代碼是不完整的,因此我們需要看看代碼測試部分的編寫。

代碼:

func ExtractUsername(email string) string {
    at := strings.Index(email, "@")
    return email[:at]
}

測試案例:

func TestExtractUsername(t *testing.T) {
    type args struct {
        email string
    }
    tests := []struct {
        name string
        args args
        want string
    }{
        {"withoutDot", args{email: "r@google.com"}, "r"},
        {"withDot", args{email: "jonh.smith@example.com"}, "jonh.smith"},
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            if got := ExtractUsername(tt.args.email); got != tt.want {
                t.Errorf("ExtractUsername() = %v, want %v", got, tt.want)
            }
        })
    }
}

6.4 創建后端服務

接下來我寫一個例子創建REST API后端服務:

我們的服務提供如下的API:

###
GET http://localhost:10000/
 
###
GET http://localhost:10000/all
 
###
GET http://localhost:10000/article/1 
 
###
POST http://localhost:10000/article HTTP/1.1
 
{
    "Id": "3",
    "Title": "Hello 2",
    "desc": "Article Description",
    "content": "Article Content"
}
 
###
PUT http://localhost:10000/article HTTP/1.1
 
{
    "Id": "2",
    "Title": "Hello 2 Update",
    "desc": "Article Description Update",
    "content": "Article Content Update"
}

完整代碼:

package main
 
import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
 
    "github.com/gorilla/mux"
)
 
type Article struct {
    Id      string `json:"Id"`
    Title   string `json:"Title"`
    Desc    string `json:"desc"`
    Content string `json:"content"`
}
 
var MapArticles map[string]Article
var Articles []Article
 
func returnAllArticles(w http.ResponseWriter, r *http.Request) {
    fmt.Println("Endpoint Hit: returnAllArticles")
    json.NewEncoder(w).Encode(Articles)
}
 
func homePage(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Welcome to the HomePage!")
    fmt.Println("Endpoint Hit: homePage")
}
 
func createNewArticle(w http.ResponseWriter, r *http.Request) {
 
    reqBody, _ := ioutil.ReadAll(r.Body)
    var article Article
    json.Unmarshal(reqBody, &article)
 
    Articles = append(Articles, article)
    MapArticles[article.Id] = article
    json.NewEncoder(w).Encode(article)
}
 
func updateArticle(w http.ResponseWriter, r *http.Request) {
 
    reqBody, _ := ioutil.ReadAll(r.Body)
    var article Article
    json.Unmarshal(reqBody, &article)
 
    found := false
    for index, v := range Articles {
        if v.Id == article.Id {
            // Found!
            found = true
            Articles[index] = article
        }
    }
    if !found {
        Articles = append(Articles, article)
    }
    MapArticles[article.Id] = article
    json.NewEncoder(w).Encode(article)
}
 
func returnSingleArticle(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    key := vars["id"]
 
    fmt.Fprintf(w, "Key: %s \n", key)
    json.NewEncoder(w).Encode(MapArticles[key])
}
 
func handleRequests() {
    myRouter := mux.NewRouter().StrictSlash(true)
    myRouter.HandleFunc("/", homePage)
    myRouter.HandleFunc("/all", returnAllArticles)
    myRouter.HandleFunc("/article", createNewArticle).Methods("POST")
    myRouter.HandleFunc("/article", updateArticle).Methods("PUT")
 
    myRouter.HandleFunc("/article/{id}", returnSingleArticle)
    log.Fatal(http.ListenAndServe(":10000", myRouter))
}
 
func main() {
    fmt.Println("Rest API is ready ...")
 
    MapArticles = make(map[string]Article)
 
    Articles = []Article{
        Article{Id: "1", Title: "Hello", Desc: "Article Description", Content: "Article Content"},
        Article{Id: "2", Title: "Hello 2", Desc: "Article Description", Content: "Article Content"},
    }
 
    for _, a := range Articles {
        MapArticles[a.Id] = a
    }
 
    handleRequests()
}
 

調用添加,更新API以后返回所有數據的測試結果:

七、【 褒貶不一】

7.1 贊揚

MicheleSimionato對Go大加贊揚:

接口系統簡潔,並刻意省略了繼承。

EngineYard的DaveAstels寫道:

Go是非常容易上手的。很少的基本語言概念,語法也很干凈,設計得很清晰。Go目前還是實驗性的,還有些地方比較粗糙。

2009年,Go被TIOBE編程社區指數評選為年度最佳編程語言。

到2010年1月,Go的排名達到了第13位,超過了Pascal等成熟的語言。

但到了2015年6月,它的排名跌至第50位以下,低於COBOL和Fortran。

但截至2017年1月,它的排名又飆升至第13位,顯示出它的普及率和采用率有了顯著的增長。

Go被評為2016年TIOBE年度最佳編程語言。

BruceEckel曾表示:

C++的復雜性(在新的C++中甚至增加了更多的復雜性),以及由此帶來的對生產力的影響,已經沒有任何理由繼續使用C++了。C++程序員為了克服C語言的一些問題而做出的增強初衷目前已經沒有了意義,而Go此時顯得更有意義。

2011年一位Google工程師R.Hundt對Go語言及其GC實現與C++(GCC)、Java和Scala的對比評估發現。

Go提供了有趣的語言特性,這也使得Go語言有了簡潔、標准化的特征。這種語言的編譯器還不成熟,這在性能和二進制大小上都有體現。

這一評價收到了Go開發團隊的快速反應。

IanLance Taylor因為Hundt的評論改進了Go代碼;

RussCox隨后對Go代碼以及C++代碼進行了優化,並讓Go代碼的運行速度比C++略快,比評論中使用的代碼性能快了一個數量級以上。

7.2 命名爭議

2009年11月10日,也就是Go!編程語言全面發布的當天,Go!編程語言的開發者FrancisMcCabe(注意是感嘆號)要求更改Google的語言名稱,以避免與他花了10年時間開發的語言混淆。

McCabe表示了對谷歌這個'大塊頭'最終會碾壓他"的擔憂,這種擔憂引起了120多名開發者的共鳴,他們在Google官方的問題線程上評論說他們應該改名,有些人甚至說這個問題違背了Google的座右銘:"不要作惡。"

2010年10月12日,谷歌開發者RussCox關閉了這個問題,自定義狀態為"不幸",並附上了以下評論:

"有很多計算產品和服務都被命名為Go。在我們發布以來的11個月里,這兩種語言的混淆度極低。"

7.3 【批評】

Go的批評家們的觀點:

  • 通用程序設計缺乏參數多態性,導致代碼重復或不安全的類型轉換以及擾亂流程的隨意性。
  • Go的nil,加上代數類型的缺失,導致故障處理和基本問題的避免很困難。
  • Go不允許在當前行中出現開局括號,這就迫使所有的Go程序員必須使用相同的括號樣式。

八、【小結】

本文從Go的語法,類型系統,編碼風格,語言工具,編碼工具和使用案例等幾方面對Go語言進行了學習和探討,希望可以拋磚引玉,對Go語言感興趣的同仁有所裨益。

 

點擊關注,第一時間了解華為雲新鮮技術~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM