@
Go 官網地址 https://golang.google.cn/ 最新版本1.20.3
Go 官網檔案地址 https://golang.google.cn/doc/
Go 原始碼地址 https://github.com/golang
Golang簡稱Go,是谷歌開源的程式語言,旨在提供程式設計師程式設計效率,易於學習,非常適合團隊使用,天然支援高並行,有垃圾收集機制,且自帶功能完善健壯的標準庫。
Go語言表現力強、簡潔、乾淨、高效。它的並行機制使得其容易編寫出充分利用多核和網路機器的程式,其型別系統使程式構造變得靈活和模組化。Go可以快速編譯為機器碼,並且具有垃圾收集的便利性和執行時反射的強大功能;是一種快速的、靜態型別的編譯語言,感覺像是一種動態型別的解釋語言。
Go語言由於來自全球技術大廠谷歌創造及推動,其生態發展極其迅速,從業界聲音看大有可能成為接下來10年內最具統治力的語言,也即是替代Java霸主地位,至於未來是否可以靜待結果。至少從目前國內大廠阿里、騰訊、百度、位元組的使用趨勢極其效應可以看到其迅速擴張的可能,越受開發者喜愛其生態完整就會越好,如果從事企業級開發的夥伴有時間精力建議的話不煩可以開始深入學學Go語言開發。
Go語言編寫安全可靠的軟體,主要有如下資訊:
先安裝最新版本的Go。有關下載和安裝Go編譯器、工具和庫的說明,請自行查詢安裝檔案,版本下載(1.20.3),https://golang.google.cn/dl/
由於之前使用1.20.2安裝,安裝後檢視版本
開發過程有需要了解相當包的使用,可以在Go開發官網上查詢,這也是一個良好學習和使用方式。
IDE整合開發工具可以選擇JetBrains的GoLand,一個擴充套件支援JavaScript, TypeScript和資料庫的Go IDE,JetBrains Go地址
# 通過命令列輸入go可以檢視支援命令
go
go help build
目前官方提供190多種標準庫包(包括cmd包),涵蓋大部分的使用場景,如壓縮、加密、資料庫、編碼、異常、畫圖、列印、IO操作、網路、作業系統、反射HTML模版等,可以先大概看下官方每個包功能大體說明,需要使用某些功能細節時再具體檢視官網。相對於標準庫,其他包也稱為子庫,這些包也是Go專案的一部分,但在Go主幹之外。它們是在比Go核心更寬鬆的相容性要求下開發的。可以用「go get」安裝它們。
Go是一門新語言,儘管它借鑑了現有語言的思想,但它具有不同尋常的特性,使有效的Go程式與用其相關語言編寫的程式在性質上有所不同。直接將c++或Java程式翻譯成Go不太可能產生令人滿意的結果。重要的是要先了解它的性質和習慣用法,瞭解用Go程式設計的既定約定比如命名、格式、程式結構等等。比如拿分號(Semicolons)來說,像C一樣,Go的形式語法使用分號來終止語句,但與C不同的是,這些分號不會出現在原始碼中。相反,詞法分析器使用一個簡單的規則在掃描時自動插入分號,因此輸入文字基本上沒有分號,也即是在Go語言中不需要採用分號結尾。
Go語言核心宗旨就是簡潔,在Go語言的相關命名也是推薦如此,命名規則涉及變數、常數、全域性函數、結構、介面、方法等的命名,從語法層面進行了以下限定;任何需要對外暴露的名字必須以大寫字母開頭類似public,不需要對外暴露的則應該以小寫字母開頭類似private。
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
}
type Host struct {
Port string `json:"port"`
Address string `json:"address"`
}
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()
}
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沒對get/set方法特別支援,必要的時候可以自己定義,Go對get有不同建議,如
// 推薦
p.FirstName()
// 不推薦
p.GetFirstName()
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風格的/* */塊註釋和c++風格的//行註釋。行註釋是常態;塊註釋主要作為包註釋出現,但在表示式中或禁用大量程式碼時很有用。
在Go語言中,變數被顯式宣告並被編譯器用來檢查函數呼叫的型別正確性;var宣告1個或多個變數。
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)
}
Go支援字元常數、字串常數、布林常數和數值常數;const宣告一個常數值。
package mainimport ( "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))}
package mainimport "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) }}
package mainimport "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") }}
package mainimport ( "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")}
package mainimport "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)}
package mainimport "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)}
package mainimport "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遍歷各種資料結構中的元素
package mainimport "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) }}
Go函數返回值需要顯式返回,即它不會自動返回最後一個表示式的值。有多個相同型別的連續引數時可以省略型別相似的引數的型別名稱,直到宣告該型別的最後一個引數。
package main
import "fmt"
// 多個引數
func plus(a int, b int) int {
return a + b
}
// 多個相同型別引數
func plusPlus(a, b, c int) int {
return a + b + c
}
// 多個返回值
func vals() (int, int) {
return 3, 7
}
// 可變引數
func sum(nums ...int) {
fmt.Print(nums, " ")
total := 0
for _, num := range nums {
total += num
}
fmt.Println(total)
}
// 閉包,Go支援匿名函數,它可以形成閉包。當想要內聯定義一個函數而不必命名它時可以使用匿名函數很。
func intSeq() func() int {
i := 0
return func() int {
i++
return i
}
}
// 遞迴函數
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支援指標,允許在程式中傳遞對值和記錄的參照。
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字串是位元組的唯讀切片。該語言和標準庫將字串特殊地視為UTF-8編碼文字的容器。在其他語言中,字串是由「字元」組成的。在go中,字元的概念被稱為符文——它是一個表示Unicode碼點的整數。
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")
}
}
Go的結構體是欄位的型別化集合,將資料分組形成記錄;結構體是可變的。
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支援在結構型別上定義的方法。方法的接受者可以是結構體也可以是指向結構體的指標。
package mainimport "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())}
介面是方法簽名的命名集合。
package mainimport ( "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支援嵌入結構體和介面,以表達更豐富的型別組合。
package mainimport "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 語言帶來了更強大的型別系統,使其可以更好地支援各種資料型別和演演算法。Go 泛型可以應用於各種資料型別和演演算法,使程式碼更加通用、簡潔、易讀和易維護。
package mainimport "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 泛型的場景:
QuickSort
函數,可以對任意型別 T 的切片進行排序。該函數採用經典的快速排序演演算法實現。在Go中,習慣上通過顯式的單獨返回值來傳達錯誤。這與Java和Ruby等語言中使用的異常以及c中有時使用的過載的單個結果/錯誤值形成對比。Go的方法可以很容易地看到哪些函數返回錯誤,並使用用於任何其他無錯誤任務的相同語言結構來處理它們。
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)
}
}
恐慌通常意味著事情出乎意料地出錯了;大多數情況下,使用它來快速處理在正常操作中不應該發生的錯誤,或者沒有準備好優雅地處理的錯誤。
package main
import "os"
func main() {
panic("a problem")
_, err := os.Create("/tmp/file")
if err != nil {
panic(err)
}
}
Defer用於確保函數呼叫在程式執行的後期執行,通常是出於清理目的。Defer通常用於其他語言中使用的地方,例如ensure和finally。比如用於釋放或關閉連線、釋放或關閉檔案控制程式碼等
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)
}
}
通過使用內建的recover函數,Go使從恐慌中恢復成為可能。恢復可以阻止因恐慌而中止程式,而是讓程式繼續執行。
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()")
}