工作这么多年之后,学习一门新的语言,对于语法方面的东西应该是只用看看就行了(重点在其生态的学习)。
所以,这只是对 Go 语法做一个简单的梳理。
25 个关键字
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
- 声明 (4):
var
变量 /const
常量 /type
类型 /func
函数 - 并发相关 (3):
go
/chan
/select
- 类型 (3):
interface
接口 /map
映射 /struct
结构体 - 循环 (4):
for
continue
break
range
用于读取 slice、map、channel 数据 - 分支 (6):
if
else
switch
case
default
fallthrough
- 包 (2):
package
/import
- 其他 (3):
defer
goto
return
预定义标识符 Predefined identifiers
https://golang.org/ref/spec#Predeclared_identifiers
https://go.dev/ref/spec#Predeclared_identifiers
或者叫保留字 Reserved words
Types:
bool byte complex64 complex128 error float32 float64
int int8 int16 int32 int64 rune string
uint uint8 uint16 uint32 uint64 uintptr
Constants:
true false iota
Zero value:
nil
Functions:
append cap close complex copy delete imag len
make new panic print println real recover
- 数据类型 (19) 参见下面基础类型部分
- 数值类型 17
string
1bool
1
- 特殊的量 4
true
/false
iota
nil
- 内建函数 (15)
- 长度和容量 2
cap
返回切片的容量len
返回切片的长度
- 切片 2
append
给切片追加元素copy
拷贝数据
- Map 1
delete
删除 map 中的元素
- 异常 2
panic
recover
- 打印 2
- print 打印
- println 打印并换行
- 复数 3
complex
复数imag
返回复数的虚部real
返回复数的实部
- 其他 3
make
创建 slice、map、channelnew
分配内存,返回指定类型的零值close
关闭 channel
- 长度和容量 2
数据类型 Types
- 不支持隐式类型转换,比如
哪怕 int 和 int64 底层相同也不行,别名都不行var a int64 = 1 var b int = a // error
参考:https://go.dev/ref/spec#Conversions - 每种数据类型关联到一组方法(Method set)
- 字面量没有类型,Golang 中叫做 Untyped Constant, 无类型常量
比如:var a int = 1.0
合法
不过要是有小数,比如var a int = 1.1
不合法,会报:constant xxx truncated to integer
。关于这一点的官方说明我没有找到。
PS: 无类型常量有一个默认类型,在:=
的时候会用到
相关文章:https://go.dev/blog/constants
基本类型
注意:常量只支持基本类型。
- Numberic 数值型
int8
,int16
,int32
,int64
uint8
,uint16
,uint32
,uint64
float32
,float64
complex64
,complex128
- 两个别名类型:
byte
=>uint8
(Byte),rune
=>int32
(Unicode) - 三种平台相关的类型:
int
,uint
,uintptr
(指针)
- String 字符串
string
- Boolean 布尔型
bool
只有两个值:true
/false
- error
默认值:
- 数值型默认值
0
- 布尔型默认值
false
- 字符串默认值
""
(空字符串)
复合类型
- Array 数组
- Slice 切片
- Struct 结构体
- Pointer 指针
- Function 函数
- Interface 接口
- Map 映射 / 关联数组 / 字典 / 哈希表
- Channel types
类型转换
s := "1"
i := 1
strconv.Atoi(s)
strconv.ParseInt(s, 10, 64) // 转 int64
var itf interface{} = 1
s, ok := itf.(string)
i, ok := itf.(int)
数组 Array & 切片 Slice
- 数组有 len 和 cap 属性,cap 是数组的容量,len 是数组的长度。这个设计使得 Go 的数组有一定的拓展性,不像 C 那么死。
如果 append 的数据太大,超出 cap 范围,就会重新分配内存。 - 切片 Slice 是一个引用类型,它指向一个底层数组,所以是共享内存的!在 Python 中切片生成新的数组。
[32]byte
[2*N] struct { x, y int32 }
[1000]*float64
[3][5]int
[2][2][2]float64 // same as [2]([2]([2]float64))
// go/src/runtime/slice.go
type slice struct {
array unsafe.Pointer
len int
cap int
}
映射 Map
map[string]int
map[*T]struct{ x, y float64 }
map[string]interface{}
make(map[string]int)
make(map[string]int, 100)
结构体 Struct
// A struct with 6 fields.
struct {
x, y int
u float32
_ float32 // padding
A *[]int
F func()
}
结构体内存对齐
struct
的大小(unsafe.Sizeof(s)
)约等于各字段大小之和,但要注意:
- Go 语言数据类型的实际内存分配是会按照字来进行,而字又和 CPU 架构相关
- 64 位 CPU:一字代表 8B
- 32 位 CPU:一字代表 4B
- 如果相同的两个字段小于一字,可能会共用一字
函数 Function
func()
func(x int) int
func(a, _ int, z float32) bool
func(a, b int, z float32) (bool)
func(prefix string, values ...int)
func(a, b int, z float64, opt ...interface{}) (success bool)
func(int, int, float64) (float64, *[]int)
func(n int) func(p *T)
接口 Interface
接口就是一组方法的集合。
所有类型都可以看作空接口(interface{}
)的实现。
// A simple File interface.
interface {
Read([]byte) (int, error)
Write([]byte) (int, error)
Close() error
}
interface {
String() string
String() string // illegal: String not unique
_(x int) // illegal: method must have non-blank name
}
管道 Channel
PS: 有的地方说管道。
三个相关关键字:
go
并发函数:协程 + 线程混合调度chan
管道:并发函数内外可以通过管道进行通讯channel <- value
阻塞发送x := <
<-channel
// 接收并将其丢弃x := <-channel
// 接收并将其保存x, ok := <-channel
// 接收并将其保存,同时检查通道是否已经关闭或者为空
select
通讯类型,作用和switch
类似,但只能用于通道(为啥不复用 switch 关键字)
// a goroutine example
func main() {
c := make(chan int) // 创建管道
go func() {
c <- 42
}()
fmt.Println(<-c)
}
package main
import (
"fmt"
"time"
)
func Producer(c chan<- int) {
for i := 0; i < 10; i++ {
c <- i
}
fmt.Println("Producer: send over")
}
func Consumer(no int, c <-chan int) {
for num := range c {
fmt.Printf("Consumer#%d: get: %v\n", no, num)
}
}
func test(c chan int) {
for i := 100; i < 110; i++ {
c <- i
}
fmt.Println("test: send over")
// 如果没有 goroutine 在活跃状态,接收channel,则提示 deadlock
// fatal error: all goroutines are asleep - deadlock!
x, ok := <-c
if !ok {
fmt.Println("test: got err")
}
fmt.Printf("test: get: %v\n", x)
}
func main() {
fmt.Println("start")
c := make(chan int, 5)
fmt.Printf("channel c: %v\n", c)
go Consumer(1, c)
fmt.Println("start Consumer 1")
go Consumer(2, c)
fmt.Println("start Consumer 2")
go Producer(c)
fmt.Println("start producer")
test(c)
fmt.Println("start test")
fmt.Println("end")
time.Sleep(time.Second * 1)
}
这个锁的情况挺有意思,下次另开一篇文章来说说。
其他
- 变量声明和初始化
- 数据类型转换
- 输出与字符串格式化
- 结构体(
struct
)和接口(interface
) - 异常处理机制
- 包管理
goroutine
- 反射
- 标准库
需要注意的点
- Go 的很多语句需要接收
err
, 然后判断是否为nil
- 函数不支持默认值参数,不定长参数传递也不如 Python 灵活(
*args, **kwargs
)
package
& import
package xxx is not in GOROOT
重点:
GOPATH
模式下,所有包在$GOPATH/src
,$GOROOT/src
Module
模式(GO111MODULE=ON
,Go 1.3+ 的默认行为)下,
详情参考 20210204, GO111MODULE
是什么?