@
-
概述
- 定義
- 使用場(chǎng)景
- Go 安全
-
使用須知
- 搜索工具
- Go基礎(chǔ)命令
- 標(biāo)準(zhǔn)庫(kù)
-
基礎(chǔ)語(yǔ)法
- Effective Go 概覽
- 命名規(guī)范
- 注釋
- 變量
- 常量(const)
- 控制結(jié)構(gòu)
- 數(shù)據(jù)類型
- 迭代(range)
- 函數(shù)
- 指針
- 字符串和符文
- 結(jié)構(gòu)體(struct)
- 方法
- 接口(interface)
- 泛型
- 錯(cuò)誤(errors)
- 恐慌(pinic)
- 推遲(defer)
- 恢復(fù)(recover)
概述
定義
Go 官網(wǎng)地址 https://golang.google.cn/ 最新版本1.20.3
Go 官網(wǎng)文檔地址 https://golang.google.cn/doc/
Go 源碼地址 https://github.com/golang
Golang簡(jiǎn)稱Go,是谷歌開(kāi)源的編程語(yǔ)言,旨在提供程序員編程效率,易于學(xué)習(xí),非常適合團(tuán)隊(duì)使用,天然支持高并發(fā),有垃圾收集機(jī)制,且自帶功能完善健壯的標(biāo)準(zhǔn)庫(kù)。
Go語(yǔ)言表現(xiàn)力強(qiáng)、簡(jiǎn)潔、干凈、高效。它的并發(fā)機(jī)制使得其容易編寫出充分利用多核和網(wǎng)絡(luò)機(jī)器的程序,其類型系統(tǒng)使程序構(gòu)造變得靈活和模塊化。Go可以快速編譯為機(jī)器碼,并且具有垃圾收集的便利性和運(yùn)行時(shí)反射的強(qiáng)大功能;是一種快速的、靜態(tài)類型的編譯語(yǔ)言,感覺(jué)像是一種動(dòng)態(tài)類型的解釋語(yǔ)言。
Go語(yǔ)言由于來(lái)自全球技術(shù)大廠谷歌創(chuàng)造及推動(dòng),其生態(tài)發(fā)展極其迅速,從業(yè)界聲音看大有可能成為接下來(lái)10年內(nèi)最具統(tǒng)治力的語(yǔ)言,也即是替代Java霸主地位,至于未來(lái)是否可以靜待結(jié)果。至少?gòu)哪壳皣?guó)內(nèi)大廠阿里、騰訊、百度、字節(jié)的使用趨勢(shì)極其效應(yīng)可以看到其迅速擴(kuò)張的可能,越受開(kāi)發(fā)者喜愛(ài)其生態(tài)完整就會(huì)越好,如果從事企業(yè)級(jí)開(kāi)發(fā)的伙伴有時(shí)間精力建議的話不煩可以開(kāi)始深入學(xué)學(xué)Go語(yǔ)言開(kāi)發(fā)。
使用場(chǎng)景
- 云上和網(wǎng)絡(luò)的應(yīng)用:如云計(jì)算領(lǐng)域、區(qū)塊鏈、并發(fā)網(wǎng)絡(luò)編程,有主要的云提供商強(qiáng)大的工具和api生態(tài)系統(tǒng),用Go構(gòu)建服務(wù)很容易。下面列舉小部分流行包:
- cloud.google.com/go
- aws/client
- Azure/azure-sdk-for-go
- Cli命令行接口:cli是純文本的。云和基礎(chǔ)設(shè)施應(yīng)用程序主要基于cli,這樣易于自動(dòng)化和遠(yuǎn)程功能。使用流行的開(kāi)放源碼包和健壯的標(biāo)準(zhǔn)庫(kù),可以使用Go創(chuàng)建快速而優(yōu)雅的cli。下面列舉小部分流行包:
- spf13/cobra
- spf13/viper
- urfave/cli
- delve
- chzyer/readline
- Web開(kāi)發(fā):Go支持快速和可擴(kuò)展的web應(yīng)用程序。下面列舉小部分流行包:
- net/http
- html/template
- flosch/pongo2
- database/sql
- elastic/go-elasticsearch
- 開(kāi)發(fā)運(yùn)營(yíng)和網(wǎng)站可靠性工程(云原生運(yùn)維方向,特別是基于k8s的運(yùn)維開(kāi)發(fā)):Go擁有快速的構(gòu)建時(shí)間、簡(jiǎn)潔的語(yǔ)法、自動(dòng)格式化器和文檔生成器,可以同時(shí)支持DevOps和SRE。下面列舉小部分流行包:
- open-telemetry/opentelemetry-go
- istio/istio
- urfave/cli
Go 安全
Go語(yǔ)言編寫安全可靠的軟件,主要有如下信息:
- Go安全策略:解釋了如何向Go團(tuán)隊(duì)報(bào)告Go標(biāo)準(zhǔn)庫(kù)和子庫(kù)中的安全問(wèn)題。
- Go安全發(fā)布:Go版本歷史包含了過(guò)去安全問(wèn)題的發(fā)布說(shuō)明。根據(jù)發(fā)布策略發(fā)布了兩個(gè)最新的Go主要版本的安全補(bǔ)丁。
- Go漏洞管理:支持幫助開(kāi)發(fā)人員找到可能影響其Go項(xiàng)目的已知公共漏洞。
- Go Fuzzing:提供一種自動(dòng)測(cè)試,持續(xù)地操作程序的輸入以發(fā)現(xiàn)錯(cuò)誤。
- Go加密:Go加密庫(kù)是Go標(biāo)準(zhǔn)庫(kù)和子庫(kù)中的crypto/…和golang.org/x/crypto/…包,并遵循這些原則開(kāi)發(fā)。
使用須知
搜索工具
Go 開(kāi)發(fā)包搜索網(wǎng)站 https://pkg.go.dev/
先安裝最新版本的Go。有關(guān)下載和安裝Go編譯器、工具和庫(kù)的說(shuō)明,請(qǐng)自行查找安裝文檔,版本下載(1.20.3),https://golang.google.cn/dl/
由于之前使用1.20.2安裝,安裝后查看版本
開(kāi)發(fā)過(guò)程有需要了解相當(dāng)包的使用,可以在Go開(kāi)發(fā)官網(wǎng)上查詢,這也是一個(gè)良好學(xué)習(xí)和使用方式。
IDE集成開(kāi)發(fā)工具可以選擇JetBrains的GoLand,一個(gè)擴(kuò)展支持JavaScript, TypeScript和數(shù)據(jù)庫(kù)的Go IDE,JetBrains Go地址
Go基礎(chǔ)命令
# 通過(guò)命令行輸入go可以查看支持命令
go
go help build
- go bug:打開(kāi)Bug報(bào)告。
- go build:用于編譯指定的源碼文件或代碼包以及它們的依賴包。常用
- go clean:移除當(dāng)前源碼包和關(guān)聯(lián)源碼包里面編譯生成的文件,即刪除掉執(zhí)行其它命令時(shí)產(chǎn)生的一些文件和目錄,go 命令會(huì)在臨時(shí)目錄中構(gòu)建對(duì)象,因此 go clean 主要關(guān)注其他工具或手動(dòng)調(diào)用 go build 留下的對(duì)象文件。常用
- go doc:打印與由其參數(shù)(包、const、func、類型、var、方法或結(jié)構(gòu)字段)標(biāo)識(shí)的項(xiàng)目相關(guān)聯(lián)的文檔注釋。
- go env:打印Go語(yǔ)言的環(huán)境信息。
- go fix:把指定代碼包的所有Go語(yǔ)言源碼文件中的舊版本代碼修正為新版本的代碼。
- go fmt:go程序格式化,自動(dòng)對(duì)齊、空格等。如果用了IDE這個(gè)命令就不需要了。
- go generate:?成由現(xiàn)有?件中的指令描述的運(yùn)?命令。這些命令可以運(yùn)?任何進(jìn)程,但?的是創(chuàng)建或更新 Go 源文件。
- go get:根據(jù)要求和實(shí)際情況從互聯(lián)網(wǎng)上下載或更新指定的代碼包及其依賴包,并對(duì)它們進(jìn)行編譯和安裝。常用
- go install:用于編譯并安裝指定的代碼包及它們的依賴包。常用
- go list:列出指定的代碼包的信息。
- go mod
- go mod init:生成go.mod文件。常用
- go mod download :下載go.mod文件中指明的所有依賴。
- go mod tidy:整理現(xiàn)有的依賴。常用
- go mod graph:查看現(xiàn)有的依賴結(jié)構(gòu)。
- go mod edit:編輯go.mod文件。
- go mod vendor :導(dǎo)出項(xiàng)目所有的依賴到vendor目錄。
- go mod verify:校驗(yàn)一個(gè)模塊是否被篡改過(guò)。
- go mod why:查看為什么需要依賴某模塊。
- go work:跨文件目錄操作本地包、多個(gè)mod模塊包調(diào)用、本地測(cè)試。
- go run:可以編譯并運(yùn)行命令源碼文件,并把編譯后的可執(zhí)行文件存放到臨時(shí)工作目錄。常用
- go test:用于以代碼包為單位對(duì)Go語(yǔ)言編寫的程序進(jìn)行測(cè)試。常用
- go tool:執(zhí)行g(shù)o自帶的工具。go tool pprof對(duì)cpu、內(nèi)存和協(xié)程進(jìn)行監(jiān)控;go tool trace跟蹤協(xié)程的執(zhí)行過(guò)程。
- go version:打印 Go 可執(zhí)??件的構(gòu)建信息。
- go vet:用于檢查Go語(yǔ)言源碼中靜態(tài)錯(cuò)誤的簡(jiǎn)單工具。
標(biāo)準(zhǔn)庫(kù)
Go 標(biāo)準(zhǔn)庫(kù) https://golang.google.cn/pkg/
精心策劃的Go項(xiàng)目列表 https://github.com/golang/go/wiki/Projects
目前官方提供190多種標(biāo)準(zhǔn)庫(kù)包(包括cmd包),涵蓋大部分的使用場(chǎng)景,如壓縮、加密、數(shù)據(jù)庫(kù)、編碼、異常、畫圖、打印、IO操作、網(wǎng)絡(luò)、操作系統(tǒng)、反射HTML模版等,可以先大概看下官方每個(gè)包功能大體說(shuō)明,需要使用某些功能細(xì)節(jié)時(shí)再具體查看官網(wǎng)。相對(duì)于標(biāo)準(zhǔn)庫(kù),其他包也稱為子庫(kù),這些包也是Go項(xiàng)目的一部分,但在Go主干之外。它們是在比Go內(nèi)核更寬松的兼容性要求下開(kāi)發(fā)的。可以用“go get”安裝它們。
基礎(chǔ)語(yǔ)法
Effective Go 概覽
Go是一門新語(yǔ)言,盡管它借鑒了現(xiàn)有語(yǔ)言的思想,但它具有不同尋常的特性,使有效的Go程序與用其相關(guān)語(yǔ)言編寫的程序在性質(zhì)上有所不同。直接將c++或Java程序翻譯成Go不太可能產(chǎn)生令人滿意的結(jié)果。重要的是要先了解它的性質(zhì)和習(xí)慣用法,了解用Go編程的既定約定比如命名、格式、程序結(jié)構(gòu)等等。比如拿分號(hào)(Semicolons)來(lái)說(shuō),像C一樣,Go的形式語(yǔ)法使用分號(hào)來(lái)終止語(yǔ)句,但與C不同的是,這些分號(hào)不會(huì)出現(xiàn)在源代碼中。相反,詞法分析器使用一個(gè)簡(jiǎn)單的規(guī)則在掃描時(shí)自動(dòng)插入分號(hào),因此輸入文本基本上沒(méi)有分號(hào),也即是在Go語(yǔ)言中不需要采用分號(hào)結(jié)尾。
命名規(guī)范
Go語(yǔ)言核心宗旨就是簡(jiǎn)潔,在Go語(yǔ)言的相關(guān)命名也是推薦如此,命名規(guī)則涉及變量、常量、全局函數(shù)、結(jié)構(gòu)、接口、方法等的命名,從語(yǔ)法層面進(jìn)行了以下限定;任何需要對(duì)外暴露的名字必須以大寫字母開(kāi)頭類似public,不需要對(duì)外暴露的則應(yīng)該以小寫字母開(kāi)頭類似private。
- 一般命名:推薦駝峰命名方式,如userManger或UserManager;單詞縮寫默認(rèn)全大寫或全小寫,如userID、baiduCDN、id、cdn。
- 項(xiàng)目名:小寫,多個(gè)單詞建議采用中劃線分隔,比如github.com/gin-gonic。
- 包名:package的名字和目錄保持一致,盡量采取有意義的包名,簡(jiǎn)短,有意義,盡量和標(biāo)準(zhǔn)庫(kù)不要沖突。包名應(yīng)該為小寫單詞,不要使用下劃線或者混合大小寫,統(tǒng)一使用單數(shù)形式。如domain、main。
- 文件名:文件命名一律采用小寫,見(jiàn)名思義,測(cè)試文件以test.go結(jié)尾,如stringutil.go, stringutil_test.go。
- Local變量:保持簡(jiǎn)短;如索引采用i而不采用index,reader簡(jiǎn)寫r,buffer簡(jiǎn)寫為b;避免冗余,命名不要和上下文重疊,在方法體內(nèi)變量名count 會(huì)比runeCount更簡(jiǎn)潔,在map的上下文下可以簡(jiǎn)寫:v,ok=m[k]。
func RuneCount(b []byte) int {
count := 0
for i := 0; i < len(b); {
if b[i] < RuneSelf {
i++
} else {
_, n := DecodeRune(b[i:])
i += n
}
count++
}
return count
}
func Read(b *Buffer, p []byte) (n int, err error) {
if b.empty() {
b.Reset()
}
n = copy(p, b.buf[b.off:])
b.off += n
return n, nil
}
- 結(jié)構(gòu)體:名詞或名詞短語(yǔ),如Account、Book,避免使用Manager這樣的。 如果該數(shù)據(jù)結(jié)構(gòu)需要序列化,如json, 則首字母大寫, 包括里面的字段。
type Host struct {
Port string `json:"port"`
Address string `json:"address"`
}
- 接口:接口單個(gè)函數(shù)的接口名以 er 為后綴,兩個(gè)函數(shù)的接口名可以綜合兩個(gè)函數(shù)名,三個(gè)以上函數(shù)的接口名類似于結(jié)構(gòu)體名。
type Reader interface {
Read(p []byte) (n int, err error)
}
type WriteFlusher interface {
Write([]byte) (int, error)
Flush() error
}
type Car interface {
Start()
Stop()
Drive()
}
- 方法:動(dòng)詞或動(dòng)詞短語(yǔ);如果是結(jié)構(gòu)體方法,那么 Receiver 的名稱應(yīng)該縮寫, 要么都使用值, 要么都用指針;一般用一個(gè)或兩個(gè)字母(優(yōu)先r);對(duì)于Receiver命名應(yīng)該統(tǒng)一, 要么都使用值, 要么都用指針,如果 Receiver 是指針可統(tǒng)一使用p。
func (b *Buffer) Read(p []byte) (n int, err error){
}
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request){
}
func (r Rectangle) Size() Point{
}
func (f foo) method() {
}
func (p *foo) method() {
}
Go沒(méi)對(duì)get/set方法特別支持,必要的時(shí)候可以自己定義,Go對(duì)get有不同建議,如
// 推薦
p.FirstName()
// 不推薦
p.GetFirstName()
- error:自定義error命名通常以 “名稱+Error” 作為結(jié)構(gòu)體的名字,變量時(shí)會(huì)用簡(jiǎn)寫err + 名稱。
type TypeError struct {
Errors []string
}
ErrShortDst = errors.New("transform: short destination buffer")
- 常用縮寫
src = source
srv = server
arg = argument
conn = connect, connection
attr = attribute
abs = absolute
min = minimum
len = length
auth = authenticate
buf = buffer
ctl = control
ctx = context
str = string
msg = message
fmt = format
dest = destination
diff = difference
orig = original
recv = receive
ref = reference
repo = repository
util = utility
注釋
Go提供C風(fēng)格的/* */塊注釋和c++風(fēng)格的//行注釋。行注釋是常態(tài);塊注釋主要作為包注釋出現(xiàn),但在表達(dá)式中或禁用大量代碼時(shí)很有用。
變量
在Go語(yǔ)言中,變量被顯式聲明并被編譯器用來(lái)檢查函數(shù)調(diào)用的類型正確性;var聲明1個(gè)或多個(gè)變量。
package main
import "fmt"
func main() {
var a = "initial"
fmt.Println(a)
var b, c int = 1, 2
fmt.Println(b, c)
var d = true
fmt.Println(d)
var e int
fmt.Println(e)
f := "apple"
fmt.Println(f)
}
常量(const)
Go支持字符常量、字符串常量、布爾常量和數(shù)值常量;const聲明一個(gè)常數(shù)值。
package main
import (
"fmt"
"math"
)
const s string = "constant"
func main() {
fmt.Println(s)
const n = 500000000
const d = 3e20 / n
fmt.Println(d)
fmt.Println(int64(d))
fmt.Println(math.Sin(n))
}
控制結(jié)構(gòu)
- 循環(huán)(For):for是Go唯一的循環(huán)結(jié)構(gòu)。
package main
import "fmt"
func main() {
i := 1
for i <= 3 {
fmt.Println(i)
i = i + 1
}
for j := 7; j <= 9; j++ {
fmt.Println(j)
}
for {
fmt.Println("loop")
break
}
for n := 0; n <= 5; n++ {
if n%2 == 0 {
continue
}
fmt.Println(n)
}
}
- 選擇(If/Else):在Go中沒(méi)有三元if,所以即使對(duì)于基本條件,也需要使用完整的if語(yǔ)句。
package main
import "fmt"
func main() {
if 7%2 == 0 {
fmt.Println("7 is even")
} else {
fmt.Println("7 is odd")
}
if 8%4 == 0 {
fmt.Println("8 is divisible by 4")
}
if num := 9; num < 0 {
fmt.Println(num, "is negative")
} else if num < 10 {
fmt.Println(num, "has 1 digit")
} else {
fmt.Println(num, "has multiple digits")
}
}
- Switch:跨多個(gè)分支表達(dá)條件。
package main
import (
"fmt"
"time"
)
func main() {
i := 2
fmt.Print("Write ", i, " as ")
switch i {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
case 3:
fmt.Println("three")
}
switch time.Now().Weekday() {
case time.Saturday, time.Sunday:
fmt.Println("It's the weekend")
default:
fmt.Println("It's a weekday")
}
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("It's before noon")
default:
fmt.Println("It's after noon")
}
whatAmI := func(i interface{}) {
switch t := i.(type) {
case bool:
fmt.Println("I'm a bool")
case int:
fmt.Println("I'm an int")
default:
fmt.Printf("Don't know type %T\n", t)
}
}
whatAmI(true)
whatAmI(1)
whatAmI("hey")
}
數(shù)據(jù)類型
- 數(shù)組(Arrays):數(shù)組是特定長(zhǎng)度的元素的編號(hào)序列。單在典型的Go代碼中,切片更為常見(jiàn),后面介紹。
package main
import "fmt"
func main() {
var a [5]int
fmt.Println("emp:", a)
a[4] = 100
fmt.Println("set:", a)
fmt.Println("get:", a[4])
fmt.Println("len:", len(a))
b := [5]int{1, 2, 3, 4, 5}
fmt.Println("dcl:", b)
var twoD [2][3]int
for i := 0; i < 2; i++ {
for j := 0; j < 3; j++ {
twoD[i][j] = i + j
}
}
fmt.Println("2d: ", twoD)
}
- 切片(Slices):是Go中的一種重要數(shù)據(jù)類型,它為序列提供了比數(shù)組更強(qiáng)大的接口。
package main
import "fmt"
func main() {
s := make([]string, 3)
fmt.Println("emp:", s)
s[0] = "a"
s[1] = "b"
s[2] = "c"
fmt.Println("set:", s)
fmt.Println("get:", s[2])
fmt.Println("len:", len(s))
s = append(s, "d")
s = append(s, "e", "f")
fmt.Println("apd:", s)
c := make([]string, len(s))
copy(c, s)
fmt.Println("cpy:", c)
l := s[2:5]
fmt.Println("sl1:", l)
l = s[:5]
fmt.Println("sl2:", l)
l = s[2:]
fmt.Println("sl3:", l)
t := []string{"g", "h", "i"}
fmt.Println("dcl:", t)
twoD := make([][]int, 3)
for i := 0; i < 3; i++ {
innerLen := i + 1
twoD[i] = make([]int, innerLen)
for j := 0; j < innerLen; j++ {
twoD[i][j] = i + j
}
}
fmt.Println("2d: ", twoD)
}
- 映射(Maps):Go內(nèi)置的關(guān)聯(lián)數(shù)據(jù)類型,在其他語(yǔ)言中有時(shí)稱為哈希或字典。
package main
import "fmt"
func main() {
m := make(map[string]int)
m["k1"] = 7
m["k2"] = 13
fmt.Println("map:", m)
v1 := m["k1"]
fmt.Println("v1:", v1)
v3 := m["k3"]
fmt.Println("v3:", v3)
fmt.Println("len:", len(m))
delete(m, "k2")
fmt.Println("map:", m)
_, prs := m["k2"]
fmt.Println("prs:", prs)
n := map[string]int{"foo": 1, "bar": 2}
fmt.Println("map:", n)
}
迭代(range)
Range遍歷各種數(shù)據(jù)結(jié)構(gòu)中的元素
package main
import "fmt"
func main() {
nums := []int{2, 3, 4}
sum := 0
for _, num := range nums {
sum += num
}
fmt.Println("sum:", sum)
for i, num := range nums {
if num == 3 {
fmt.Println("index:", i)
}
}
kvs := map[string]string{"a": "apple", "b": "banana"}
for k, v := range kvs {
fmt.Printf("%s -> %s\n", k, v)
}
for k := range kvs {
fmt.Println("key:", k)
}
for i, c := range "go" {
fmt.Println(i, c)
}
}
函數(shù)
Go函數(shù)返回值需要顯式返回,即它不會(huì)自動(dòng)返回最后一個(gè)表達(dá)式的值。有多個(gè)相同類型的連續(xù)參數(shù)時(shí)可以省略類型相似的參數(shù)的類型名稱,直到聲明該類型的最后一個(gè)參數(shù)。
package main
import "fmt"
// 多個(gè)參數(shù)
func plus(a int, b int) int {
return a + b
}
// 多個(gè)相同類型參數(shù)
func plusPlus(a, b, c int) int {
return a + b + c
}
// 多個(gè)返回值
func vals() (int, int) {
return 3, 7
}
// 可變參數(shù)
func sum(nums ...int) {
fmt.Print(nums, " ")
total := 0
for _, num := range nums {
total += num
}
fmt.Println(total)
}
// 閉包,Go支持匿名函數(shù),它可以形成閉包。當(dāng)想要內(nèi)聯(lián)定義一個(gè)函數(shù)而不必命名它時(shí)可以使用匿名函數(shù)很。
func intSeq() func() int {
i := 0
return func() int {
i++
return i
}
}
// 遞歸函數(shù)
func fact(n int) int {
if n == 0 {
return 1
}
return n * fact(n-1)
}
func main() {
res := plus(1, 2)
fmt.Println("1+2 =", res)
res = plusPlus(1, 2, 3)
fmt.Println("1+2+3 =", res)
a, b := vals()
fmt.Println(a)
fmt.Println(b)
_, c := vals()
fmt.Println(c)
sum(1, 2)
sum(1, 2, 3)
nums := []int{1, 2, 3, 4}
sum(nums...)
nextInt := intSeq()
fmt.Println(nextInt())
fmt.Println(nextInt())
fmt.Println(nextInt())
newInts := intSeq()
fmt.Println(newInts())
fmt.Println(fact(7))
var fib func(n int) int
fib = func(n int) int {
if n < 2 {
return n
}
return fib(n-1) + fib(n-2)
}
fmt.Println(fib(7))
}
指針
Go支持指針,允許在程序中傳遞對(duì)值和記錄的引用。
package main
import "fmt"
func zeroval(ival int) {
ival = 0
}
func zeroptr(iptr *int) {
*iptr = 0
}
func main() {
i := 1
fmt.Println("initial:", i)
zeroval(i)
fmt.Println("zeroval:", i)
zeroptr(&i)
fmt.Println("zeroptr:", i)
fmt.Println("pointer:", &i)
}
字符串和符文
Go字符串是字節(jié)的只讀切片。該語(yǔ)言和標(biāo)準(zhǔn)庫(kù)將字符串特殊地視為UTF-8編碼文本的容器。在其他語(yǔ)言中,字符串是由“字符”組成的。在go中,字符的概念被稱為符文——它是一個(gè)表示Unicode碼點(diǎn)的整數(shù)。
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
const s = "??????"
fmt.Println("Len:", len(s))
for i := 0; i < len(s); i++ {
fmt.Printf("%x ", s[i])
}
fmt.Println()
fmt.Println("Rune count:", utf8.RuneCountInString(s))
for idx, runeValue := range s {
fmt.Printf("%#U starts at %d\n", runeValue, idx)
}
fmt.Println("\nUsing DecodeRuneInString")
for i, w := 0, 0; i < len(s); i += w {
runeValue, width := utf8.DecodeRuneInString(s[i:])
fmt.Printf("%#U starts at %d\n", runeValue, i)
w = width
examineRune(runeValue)
}
}
func examineRune(r rune) {
if r == 't' {
fmt.Println("found tee")
} else if r == '?' {
fmt.Println("found so sua")
}
}
結(jié)構(gòu)體(struct)
Go的結(jié)構(gòu)體是字段的類型化集合,將數(shù)據(jù)分組形成記錄;結(jié)構(gòu)體是可變的。
package main
import "fmt"
type person struct {
name string
age int
}
func newPerson(name string) *person {
p := person{name: name}
p.age = 42
return &p
}
func main() {
fmt.Println(person{"Bob", 20})
fmt.Println(person{name: "Alice", age: 30})
fmt.Println(person{name: "Fred"})
fmt.Println(&person{name: "Ann", age: 40})
fmt.Println(newPerson("Jon"))
s := person{name: "Sean", age: 50}
fmt.Println(s.name)
sp := &s
fmt.Println(sp.age)
sp.age = 51
fmt.Println(sp.age)
}
方法
Go支持在結(jié)構(gòu)類型上定義的方法。方法的接受者可以是結(jié)構(gòu)體也可以是指向結(jié)構(gòu)體的指針。
package main
import "fmt"
type rect struct {
width, height int
}
func (r *rect) area() int {
return r.width * r.height
}
func (r rect) perim() int {
return 2*r.width + 2*r.height
}
func main() {
r := rect{width: 10, height: 5}
fmt.Println("area: ", r.area())
fmt.Println("perim:", r.perim())
rp := &r
fmt.Println("area: ", rp.area())
fmt.Println("perim:", rp.perim())
}
接口(interface)
接口是方法簽名的命名集合。
package main
import (
"fmt"
"math"
)
type geometry interface {
area() float64
perim() float64
}
type rect struct {
width, height float64
}
type circle struct {
radius float64
}
func (r rect) area() float64 {
return r.width * r.height
}
func (r rect) perim() float64 {
return 2*r.width + 2*r.height
}
func (c circle) area() float64 {
return math.Pi * c.radius * c.radius
}
func (c circle) perim() float64 {
return 2 * math.Pi * c.radius
}
func measure(g geometry) {
fmt.Println(g)
fmt.Println(g.area())
fmt.Println(g.perim())
}
func main() {
r := rect{width: 3, height: 4}
c := circle{radius: 5}
measure(r)
measure(c)
}
Go支持嵌入結(jié)構(gòu)體和接口,以表達(dá)更豐富的類型組合。
package main
import "fmt"
type base struct {
num int
}
func (b base) describe() string {
return fmt.Sprintf("base with num=%v", b.num)
}
type container struct {
base
str string
}
func main() {
co := container{
base: base{
num: 1,
},
str: "some name",
}
fmt.Printf("co={num: %v, str: %v}\n", co.num, co.str)
fmt.Println("also num:", co.base.num)
fmt.Println("describe:", co.describe())
type describer interface {
describe() string
}
var d describer = co
fmt.Println("describer:", d.describe())
}
泛型
Go 1.18 引入了泛型,為 Go 語(yǔ)言帶來(lái)了更強(qiáng)大的類型系統(tǒng),使其可以更好地支持各種數(shù)據(jù)類型和算法。Go 泛型可以應(yīng)用于各種數(shù)據(jù)類型和算法,使代碼更加通用、簡(jiǎn)潔、易讀和易維護(hù)。
package main
import "fmt"
func MapKeys[K comparable, V any](m map[K]V) []K {
r := make([]K, 0, len(m))
for k := range m {
r = append(r, k)
}
return r
}
type List[T any] struct {
head, tail *element[T]
}
type element[T any] struct {
next *element[T]
val T
}
func (lst *List[T]) Push(v T) {
if lst.tail == nil {
lst.head = &element[T]{val: v}
lst.tail = lst.head
} else {
lst.tail.next = &element[T]{val: v}
lst.tail = lst.tail.next
}
}
func (lst *List[T]) GetAll() []T {
var elems []T
for e := lst.head; e != nil; e = e.next {
elems = append(elems, e.val)
}
return elems
}
func main() {
var m = map[int]string{1: "2", 2: "4", 4: "8"}
fmt.Println("keys:", MapKeys(m))
_ = MapKeys[int, string](m)
lst := List[int]{}
lst.Push(10)
lst.Push(13)
lst.Push(23)
fmt.Println("list:", lst.GetAll())
}
以下是一些使用 Go 泛型的場(chǎng)景:
- 集合數(shù)據(jù)類型:Go 泛型可以方便地定義各種集合數(shù)據(jù)類型,如棧、隊(duì)列、鏈表、二叉樹(shù)等。下面使用泛型定義棧的例子定義了一個(gè) Stack[T] 類型,表示一個(gè)可以存儲(chǔ)任意類型 T 的棧。Push 方法用于將一個(gè)元素壓入棧中,Pop 方法用于彈出棧頂?shù)脑亍?/li>
-
算法實(shí)現(xiàn):泛型還可以用于實(shí)現(xiàn)各種算法,如排序、查找、字符串匹配等。以下是一個(gè)使用泛型實(shí)現(xiàn)快速排序的例子:在這個(gè)例子中,使用泛型定義
QuickSort
函數(shù),可以對(duì)任意類型 T 的切片進(jìn)行排序。該函數(shù)采用經(jīng)典的快速排序算法實(shí)現(xiàn)。
錯(cuò)誤(errors)
在Go中,習(xí)慣上通過(guò)顯式的單獨(dú)返回值來(lái)傳達(dá)錯(cuò)誤。這與Java和Ruby等語(yǔ)言中使用的異常以及c中有時(shí)使用的重載的單個(gè)結(jié)果/錯(cuò)誤值形成對(duì)比。Go的方法可以很容易地看到哪些函數(shù)返回錯(cuò)誤,并使用用于任何其他無(wú)錯(cuò)誤任務(wù)的相同語(yǔ)言結(jié)構(gòu)來(lái)處理它們。
package main
import (
"errors"
"fmt"
)
func f1(arg int) (int, error) {
if arg == 42 {
return -1, errors.New("can't work with 42")
}
return arg + 3, nil
}
type argError struct {
arg int
prob string
}
func (e *argError) Error() string {
return fmt.Sprintf("%d - %s", e.arg, e.prob)
}
func f2(arg int) (int, error) {
if arg == 42 {
return -1, &argError{arg, "can't work with it"}
}
return arg + 3, nil
}
func main() {
for _, i := range []int{7, 42} {
if r, e := f1(i); e != nil {
fmt.Println("f1 failed:", e)
} else {
fmt.Println("f1 worked:", r)
}
}
for _, i := range []int{7, 42} {
if r, e := f2(i); e != nil {
fmt.Println("f2 failed:", e)
} else {
fmt.Println("f2 worked:", r)
}
}
_, e := f2(42)
if ae, ok := e.(*argError); ok {
fmt.Println(ae.arg)
fmt.Println(ae.prob)
}
}
恐慌(pinic)
恐慌通常意味著事情出乎意料地出錯(cuò)了;大多數(shù)情況下,使用它來(lái)快速處理在正常操作中不應(yīng)該發(fā)生的錯(cuò)誤,或者沒(méi)有準(zhǔn)備好優(yōu)雅地處理的錯(cuò)誤。
package main
import "os"
func main() {
panic("a problem")
_, err := os.Create("/tmp/file")
if err != nil {
panic(err)
}
}
推遲(defer)
Defer用于確保函數(shù)調(diào)用在程序執(zhí)行的后期執(zhí)行,通常是出于清理目的。Defer通常用于其他語(yǔ)言中使用的地方,例如ensure和finally。比如用于釋放或關(guān)閉連接、釋放或關(guān)閉文件句柄等
package main
import (
"fmt"
"os"
)
func main() {
f := createFile("d:/tmp/defer.txt")
defer closeFile(f)
writeFile(f)
}
func createFile(p string) *os.File {
fmt.Println("creating")
f, err := os.Create(p)
if err != nil {
panic(err)
}
return f
}
func writeFile(f *os.File) {
fmt.Println("writing")
fmt.Fprintln(f, "data")
}
func closeFile(f *os.File) {
fmt.Println("closing")
err := f.Close()
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
}
恢復(fù)(recover)
通過(guò)使用內(nèi)置的recover函數(shù),Go使從恐慌中恢復(fù)成為可能?;謴?fù)可以阻止因恐慌而中止程序,而是讓程序繼續(xù)執(zhí)行。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-428518.html
package main
import "fmt"
func mayPanic() {
panic("a problem")
}
func main() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered. Error:\n", r)
}
}()
mayPanic()
fmt.Println("After mayPanic()")
}
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-428518.html
- 本人博客網(wǎng)站IT小神 www.itxiaoshen.com
到了這里,關(guān)于云原生時(shí)代崛起的編程語(yǔ)言Go基礎(chǔ)實(shí)戰(zhàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!