golang 要点速记
go 语法要点,持续更新
用了半年 Rust 之后发现 go 的语法基础知识有点生疏了,于是把官方 tutorial 又翻看了一遍,把其中一些比较重要关键的点列举出来了方便以后回顾,后续有新的要点也会继续补充进来。
1. 闭包
// 闭包实现狄波拉切数列
package main
import "fmt"
// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {
num1, num2 := 0, 1
return func() int {
defer func() {num1, num2 = num2, num1 + num2}()
return num1
}
}
func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}
2. 方法
type P struct {
x, y float64
}
// p called reciver(接收器)
func (p P) Print() {
fmt.Println(p.x, p.y)
}
- method 定义只能在 receiver 定义的 package 里面声明和定义
- go 默认是值传递,所以 receiver 是
*T
和T
两种类型时是有区别的 - method receiver 是
*T
,go 解释器默认会自动转换成 pointer receiver 即(&T).Method()
- method receiver 是
T
,go 解释器默认会自动转换成 value receiver 即(*T).Method()
- pointer receiver 有两个优势:1.修改 receiver 本身的值;2.避免 value copy 的开销
- pointer receiver 和 value receiver 不要混用
3. interface
// interface is a set of method signatures
type ABC interface {
Method_A() int
Method_B() float64
// ...
}
// any type which implement all of the methods can be the interface type
type A struct {
A int
B float64
}
func (A) Method_A() int {
return A.A
}
func (A) Method_B() float64 {
return A.B
}
interface{}
被称为 empty interface,可以指向任意类型switch i.(type){}
做类型判断t, ok := i.(T)
做某个类型判断
4. goroutine
func do_something() {
time.Sleep(100 * time.Millisecond)
}
func main() {
go do_something()
do_something()
}
5. channel
package main
import "fmt"
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // send sum to c
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x+y)
}
- chan 的 sender,receiver 默认会阻塞直到对端有值进出
make(chan int, 100)
称之为 buffer channel,只有 buffer 满的时候 sender 才会阻塞,同时只有 buffer 空的时候 receiver 才会阻塞- 只有 sender 可以
close
- 发送内容给一个 close 的 channel 会触发 panic
v, ok := <-ch
可以检测 channel 是否 closefor i := range c
可以重复的从 channel c 中获取值直到 channel close- 一般而言 channel 不需要特意关闭(跟文件 open/close 不一样),除非有必要 receiver 要被告知 channel 关闭了
6. select
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
- select 可以等待多个通信操作(channel)
- select 一致阻塞直到某个 case 可以运行
- 如果有多个 case 可以运行,select 会随机选择一个执行
default
case 用于处理没有 case 可以运行的情况
7. Mutex
package main
import (
"fmt"
"sync"
"time"
)
// SafeCounter is safe to use concurrently.
type SafeCounter struct {
mu sync.Mutex
v map[string]int
}
// Inc increments the counter for the given key.
func (c *SafeCounter) Inc(key string) {
c.mu.Lock()
// Lock so only one goroutine at a time can access the map c.v.
c.v[key]++
c.mu.Unlock()
}
// Value returns the current value of the counter for the given key.
func (c *SafeCounter) Value(key string) int {
c.mu.Lock()
// Lock so only one goroutine at a time can access the map c.v.
defer c.mu.Unlock()
return c.v[key]
}
func main() {
c := SafeCounter{v: make(map[string]int)}
for i := 0; i < 1000; i++ {
go c.Inc("somekey")
}
time.Sleep(time.Second)
fmt.Println(c.Value("somekey"))
}
sync.Mutex
用来做资源访问互斥操作
8. 基本语法要点
- go 代码都是由 package 构成,import package 进行依赖
- go 通过首字母是否大写控制对外的可见性,称之为 exported names
- func 参数简写:
x int, y int --> x, y int
- func 返回值可以有明明:
func foo() (x,y int){...}
- var 声明一个变量
- := 也可以声明一个变量,但是 const 变量不可以用这种方式声明
- go 类型强制转换方法:
T(v)
- go 循环控制只有一种
for
- for 循环的除了判断条件其他都可以省略
- while 循环实现:
for sum < 100 // while loops
- 死循环实现:
for {}
- if 条件判断可以在条件之前加一个简单的语句:
if v := 0; v < 10 {}
- switch-case 可以用来简化 if-else 语句:
// i ops
// simplify if-else statement with switch-case
switch {
case i == 1:
// do something
case i == 2:
// do something
case i > 3:
// do something
default:
// do somthing
}
- go 的 switch 在执行完一个 case 之后会自动停止,所以不需要显示的添加 break
- defer 会在函数 return 之后自动执行
- defer 会按照 LIFO 的顺序执行(stack)
- slice index 是一个左闭又开的区间(half-open range):
a[1:4] // 表示 a[1], a[2], a[3] 三个元素
Public discussion