摘要:本文从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格调的并发性。
它的域名是http://golang.org,所以通常被称为"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 "或"http://golang.org/x/net/html")和一个名称(如bzip2或html)。
对其余包的定义的援用必须始终以其余包的名称作为前缀,并且只有其余包的大写的名称能力被拜访:io.Reader是公开的,但bzip2.reader不是。
go get命令能够检索存储在近程资源库中的包,激励开发者在开发包时,在与源资源库绝对应的根底门路
(如http://example.com/user_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。
谷歌,许多我的项目,特地是下载服务器http://dl.google.com。
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]Articlevar 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语言感兴趣的同仁有所裨益。
点击关注,第一工夫理解华为云陈腐技术~