作为一个 BanG Dream! It’s MyGO!!!!!的忠实粉丝和 Counter-Strike: Global Offensive 的忠实玩家,Go 语言自然也是必不可少的
本文记录了学习 Go 官网中的Go by Example 的笔记和一些从别的地方的东西
basic 先看一个例子
1 2 3 4 5 6 7 8 9 10 11 12 13 package  mainimport  (    "fmt"      "math"  ) func  main ()     fmt.Println("go"  + "lang" )     fmt.Println("1+1 =" , 1 +1 )     fmt.Println("7.0/3.0 =" , 7.0 /3.0 )     fmt.Println(true  && false )     fmt.Println(true  || false )     fmt.Println(!true ) } 
看起来第一行代表使用 package main 声明这个文件属于 main 包
fmt代表输入输出的包
run 使用
来编译运行
var 可以使用 var 变量名 类型,如
声明一个
仅在函数内部可以使用:=,如
可以类型推断,也能指定
1 2 var  a int  = 10 var  b = 20 
以及用来枚举的 iota //todo
1 2 3 4 5 const  (    Sunday = iota      Monday     Tuesday ) 
string 首先, 在 fmt 中有 String()方法,可以用来输出 string
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package  mainimport  (	"fmt"  	 ) type  coordinate struct  {	d, m, s float64  	h       rune  } func  (c coordinate) string  {	return  fmt.Sprintf("%g° %g'%g\" %c" , c.d, c.m, c.s, c.h) } func  main () 	lat := coordinate{d: 4 , m: 30 , s: 0.0 , h: 'N' } 	long := coordinate{d: 135 , m: 54 , s: 0.0 , h: 'E' } 	fmt.Printf("Elysium Planitia is at %s, %s\n" , lat, long) } 
还可以导入 strings 包来进行 string 的操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package  mainimport  (    "fmt"      s "strings"  ) var  p = fmt.Printlnfunc  main ()     p("Contains:  " , s.Contains("test" , "es" ))     p("Count:     " , s.Count("test" , "t" ))     p("HasPrefix: " , s.HasPrefix("test" , "te" ))     p("HasSuffix: " , s.HasSuffix("test" , "st" ))     p("Index:     " , s.Index("test" , "e" ))     p("Join:      " , s.Join([]string {"a" , "b" }, "-" ))     p("Repeat:    " , s.Repeat("a" , 5 ))     p("Replace:   " , s.Replace("foo" , "o" , "0" , -1 ))     p("Replace:   " , s.Replace("foo" , "o" , "0" , 1 ))     p("Split:     " , s.Split("a-b-c-d-e" , "-" ))     p("ToLower:   " , s.ToLower("TEST" ))     p("ToUpper:   " , s.ToUpper("test" )) } 
输出为
1 2 3 4 5 6 7 8 9 10 11 12 13 $ go run string-functions.go Contains:   true Count:      2 HasPrefix:  true HasSuffix:  true Index:      1 Join:       a-b Repeat:     aaaaa Replace:    f00 Replace:    f0o Split:      [a b c d e] ToLower:    test ToUpper:    TEST 
For if/else switch for 有许多写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package  mainimport  "fmt" func  main ()     i := 1      for  i <= 3  {         fmt.Println(i)         i = i + 1      }     for  j := 0 ; j < 3 ; j++ {         fmt.Println(j)     }     for  i := range  3  {         fmt.Println("range" , i)     }     for  {         fmt.Println("loop" )         break      }     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" )     }     jk := 2      fmt.Print("Write " , jk, " as " )     switch  jk {     case  1 :         fmt.Println("one" )     case  2 :         fmt.Println("two" )     case  3 :         fmt.Println("three" )     } } 
反正就和 c 语言把小括号去了差不多
Arrays 1 2 3 4 5 6 7 var  a [5 ]int b = [...]int {100 , 3 : 400 , 500 } twoD = [2 ][3 ]int {     {1 , 2 , 3 },     {1 , 2 , 3 }, } 
Slices:类似于可变长数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16    c := make ([]string , len (s))    copy (c, s)    l = s[:5 ]    fmt.Println("sl2:" , l)    l = s[2 :]    fmt.Println("sl3:" , l)        twoD := make ([][]int , 3 )    for  i := range  twoD {        twoD[i] = make ([]int , 4 )    } 
maps 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 func  main ()     set := make (map [string ]bool )          set["apple" ] = true      set["banana" ] = true           if  set["apple" ] {         fmt.Println("Apple is in the set" )     }          delete (set, "banana" )          for  item := range  set {         fmt.Println(item)     } } func  main ()     users := map [string ]map [string ]string {         "alice" : {             "email" : "alice@example.com" ,             "phone" : "123-456-7890" ,         },         "bob" : {             "email" : "bob@example.com" ,             "phone" : "098-765-4321" ,         },     }     fmt.Println("Alice's email:" , users["alice" ]["email" ])          users["charlie" ] = map [string ]string {         "email" : "charlie@example.com" ,         "phone" : "111-222-3333" ,     }          for  name, info := range  users {         fmt.Printf("%s: Email - %s, Phone - %s\n" , name, info["email" ], info["phone" ])     } } 
Functions 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package  mainimport  "fmt" func  plus (a int , b int ) int  {    return  a + b } func  plusPlus (a, b, c int ) int  {    return  a + b + c } func  main ()     res := plus(1 , 2 )     fmt.Println("1+2 =" , res)     res = plusPlus(1 , 2 , 3 )     fmt.Println("1+2+3 =" , res) } 
Variadic Functions 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package  mainimport  "fmt" func  sum (nums ...int )     fmt.Print(nums, " " )     total := 0      for  _, num := range  nums {         total += num     }     fmt.Println(total) } func  main ()     nums := []int {1 , 2 , 3 , 4 }     sum(nums...) } 
Closure 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package  mainimport  "fmt" func  intSeq () func () int  {    i := 0      return  func () int  {         i++         return  i     } } func  main ()     nextInt := intSeq()     fmt.Println(nextInt())     fmt.Println(nextInt())     fmt.Println(nextInt())     newInts := intSeq()     fmt.Println(newInts()) } 
oop struct 和 c 的差不多
method 感觉这个写法有点神人
就是与特定类型关联的函数,比如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package  mainimport  "fmt" type  rect struct  {    width, height int  } func  (r *rect) int  {    return  r.width * r.height } func  (r rect) 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()) } 
还是要注意指针和值
会在改变 rp 的时候改变 r 的值
interface 约定俗成:一般接口名字结尾用 er
和别的语言也差不多,这就是 go 中的多态的实现
关于其他:没有 class,对象,继承,面向对象就用 method 和 interface 的组合来实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 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) float64  {    return  r.width * r.height } func  (r rect) float64  {    return  2 *r.width + 2 *r.height } func  (c circle) float64  {    return  math.Pi * c.radius * c.radius } func  (c circle) 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) } 
struct 嵌入实现转发 在 struct 中只给定字段类型,不给定字段名即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package  mainimport  (	"fmt"  ) type  report struct  {	sol int  	temperture 	location } type  temperture struct  {	high, low celsius } type  location struct  {	lat, long float64  } type  celsius float64 func  (t temperture) 	return  (t.high + t.low) / 2  } func  main () 	bradbury := location{-4.5895 , 137.4417 } 	t := temperture{high: -1.0 , low: -78.0 } 	report := report{ 		sol:        15 , 		temperture: t, 		location:   bradbury, 	} 	fmt.Println(report.average()) 	 } 
error nil 声明为接口的变量未被赋值的时候零值是 nil
接口类型的变量只有在类型和值都是 nil 的时候才为 nil
例题:
• 亚瑟被一位骑士挡住了去路。正如 leftHand item 变量的值 nil 所示,这位英雄手上正空无一物。 item) 和 give(to *character) 等方法的 character 结构,然后使用你在本节学到的知识编写一个脚本,使得亚瑟可以拿起一件物品并将其交给骑士,与此同时为每个动作打印出适当的描述。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package  mainimport  (	"fmt"  ) type  item struct  {	name string  } type  character struct  {	name     string  	leftHand *item } func  (c *character) 	if  c.leftHand != nil  { 		fmt.Printf("%s's hand is already occupied.\n" , c.name) 		return  	} 	c.leftHand = i 	fmt.Printf("%s picks up %s.\n" , c.name, i.name) } func  (c *character) 	if  c.leftHand == nil  { 		fmt.Printf("%s has nothing to give.\n" , c.name) 		return  	} 	if  to.leftHand != nil  { 		fmt.Printf("%s cannot accept %s; hand is occupied.\n" , to.name, c.leftHand.name) 		return  	} 	fmt.Printf("%s gives %s to %s.\n" , c.name, c.leftHand.name, to.name) 	to.leftHand = c.leftHand 	c.leftHand = nil  } func  main () 	 	arthur := &character{name: "Arthur" } 	knight := &character{name: "The Knight" } 	sword := &item{name: "Excalibur" } 	 	arthur.pickup(sword) 	arthur.give(knight) } 
error 按照惯例函数返回最后一个值应该是表示错误
可以用 New 来创建一个创建一个新的、简单的 error
1 var ErrDivideByZero = errors.New("cannot divide by zero") 
这样后面就可以用
1 2 3 4 5 6 7 8 if err == ErrDivideByZero { fmt.Println("Error:", err) } else { fmt.Println("Unexpected error:", err) } } else { fmt.Println("Result:", result) } 
自定义错误类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package  mainimport  (    "errors"      "fmt"  ) type  argError struct  {    arg     int      message string  } func  (e *argError) string  {    return  fmt.Sprintf("%d - %s" , e.arg, e.message) } func  f (arg int ) int , error ) {    if  arg == 42  {         return  -1 , &argError{arg, "can't work with it" }     }     return  arg + 3 , nil  } func  main ()     _, err := f(42 )     var  ae *argError     if  errors.As(err, &ae) {         fmt.Println(ae.arg)         fmt.Println(ae.message)     } else  {         fmt.Println("err doesn't match argError" )     } } 
defer 允许推迟函数的执行直到当前函数返回。这在处理资源清理、锁的释放、文件关闭等场景中特别有用
defer语句的执行时机在函数返回之前,即使它位于return语句之后:
多个 defer 语句,它们会以后进先出(LIFO)的顺序执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package  mainimport  "fmt" func  main ()     fmt.Println("Enter main" )     defer  fmt.Println("Defer in main - 1" )     func ()          fmt.Println("Enter anonymous function" )         defer  fmt.Println("Defer in anonymous function - 1" )         defer  fmt.Println("Defer in anonymous function - 2" )         fmt.Println("Exit anonymous function" )     }()     deferOrder()     defer  fmt.Println("Defer in main - 2" )     fmt.Println("Exit main" ) } func  deferOrder ()     defer  fmt.Println("Defer in deferOrder - 1" )     defer  fmt.Println("Defer in deferOrder - 2" )     fmt.Println("In deferOrder function" ) } 
输出为
1 2 3 4 5 6 7 8 9 10 11 Enter main Enter anonymous function Exit anonymous function Defer in anonymous function - 2 Defer in anonymous function - 1 In deferOrder function Defer in deferOrder - 2 Defer in deferOrder - 1 Exit main Defer in main - 2 Defer in main - 1 
recover:用来处理 panic 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package  mainimport  "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()" ) } 
输出为
1 2 3 $ go run recover.go Recovered. Error:  a problem 
concurrent goroutine Goroutine 是 Go 语言中最基本的并发执行单元。它是一个轻量级的”线程”,由 Go 运行时管理
创建一个 goroutine 非常简单,只需要在函数调用前加上go关键字:
1 2 3 4 5 6 7 8 func  hello ()     fmt.Println("Hello from goroutine!" ) } func  main ()     go  hello()     time.Sleep(1  * time.Second)   } 
Channel Channel 是 goroutine 之间通信的管道。它可以让一个 goroutine 发送特定值到另一个 goroutine
基本用法
1 2 3 4 5 6 7 8 9 10 11 12 func main() {     // 创建一个整数类型的通道     ch := make(chan int)     // 在goroutine中发送数据     go func() {         ch <- 42  // 发送数据到通道     }()     value := <-ch  // 从通道接收数据     fmt.Println(value) } 
Channel 的类型:
无缓冲通道:
有缓冲通道:
 
同步机制 Go 提供了多种同步机制:
WaitGroup 用于等待一组 goroutine 完成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 var  wg sync.WaitGroupfunc  worker (id int )     defer  wg.Done()     fmt.Printf("Worker %d starting\n" , id)     time.Sleep(time.Second)     fmt.Printf("Worker %d done\n" , id) } func  main ()     for  i := 1 ; i <= 3 ; i++ {         wg.Add(1 )         go  worker(i)     }     wg.Wait() } 
Mutex(互斥锁) 用于保护共享资源:
1 2 3 4 5 6 7 8 9 10 var  (    mutex sync.Mutex     counter int  ) func  increment ()     mutex.Lock()     counter++     mutex.Unlock() } 
Select Select 语句可以同时等待多个通道操作:
1 2 3 4 5 6 7 8 select  {case  msg1 := <-ch1:    fmt.Println("Received from ch1:" , msg1) case  msg2 := <-ch2:    fmt.Println("Received from ch2:" , msg2) case  <-time.After(time.Second):    fmt.Println("Timeout" ) }