在Go中,如果一个名称以大写字母开头,则它为已导出的名称
在导入一个包后,只能使用这个包中已导出的名字,任何未导出的名字在这个包外无法访问
函数
func add(x int, y int) int {
return x + y
}
函数可以返回任意数量的返回值
func swap(x, y string) (string, string) {
return y, x
}
变量
var用于声明一个变量列表
var a, b, c bool
简洁赋值语句 := 可以在类型明确的地方代替 var 声明
a, b, c := true, false, "giao"
:=不能在函数外使用
Go 数据类型
bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte(alias of uint8)
rune(alias of int32)
float32 float64
complex64 complex128
变量声明可以写成一个语法块
var (
a bool = true
b int = 123
c complex128 = cmplx.Sqrt(1 + 2i)
)
类型转换
a := 42
b := float64(a)
u := uint(b)
类型推导
声明一个变量时,如果不指定其类型,则类型由右值推导而出
var i int
j := i // j 为 int
常量
常量使用const关键字声明
常量不能使用:=声明
循环
for i := 0; i < 10; i++ {
...
}
for 循环 range 形式
for i, v := range pow {
...
}
range 每次迭代都会返回两个值,一个是当前元素的下标,另一个是对应元素的副本
if
if x < 0 {
...
} else {
...
}
// 前置语句
if v := 0; v < 0 {
...
}
v 的作用范围只在 if 内
switch
无条件switch
t := time.Now()
switch t {
case t.Hour() < 12:
println("good morning")
case t.Hour() = 12:
println("good noon")
case t.Hour() < 18:
println("good afternoon")
default:
println("good evening")
}
defer
defer 语句会将函数的执行推迟到外层函数返回之后
推迟的函数调用会被放入栈中,当外层函数返回时,被推迟的函数会按照后进先出的顺序调用
func main() {
println("counting")
for i := 0; i < 10; i++ {
defer println(i)
}
}
指针
指针保存值的内存地址
var p *int
对变量使用 & 会生成一个指向该变量得指针
int i := 123
p = &i
通过指针读取或赋值
println(*p)
*p = 456
Go 没有指针运算
结构体
一个结构体就是一组字段
type Vertex struct {
X int
Y int
}
v := Vertex{1, 2}
print(v)
print(v.X)
print(v.Y)
结构体指针
vp = &v
print(*vp)
print(vp.X)
print(vp.Y)
数组
var a [10]int
var b []int = {..., ..., ...}
数组的长度是固定的
切片
切片通过两个下标来确定,即切片的上界和下界
a[1 : 4] // 其包含a中下标从1到3的元素
切片不存储任何数据,它类似对应数组的引用,更改切片的元素会导致对应数组的元素也被更改
数组文法
[3]bool{true, true, false}
切片文法
[]bool{true, true, false}
以上会创建一个和相同的数组,然后构建一个引用了它的切片
对于var a [10]来说
a[0:10]
a[:10]
a[0:]
a[:]
以上切片是等价的
切片的长度
len(s)
切片的容量
cap(s)
切片的零值
nil // 切片的长度和容量为0且没有底层数组
使用make创建切片
a := make([]int, 5) // 创建一个元素为0值的数组,并返回一个引用了它的切片
a := make([]int, 0, 5) // 第三个参数指定切片的容量
b = b[:cap(b)]
为切片追加新的元素
append(s, ...) // 第一个参数是切片,其余为待添加的元素
映射
var m map[string]Vertex
初始化
m = make(map[string]Vertex)
获取元素
m["giao"] = Vertex{1, 2}
删除元素
delete(m, "giao")
通过双赋值检查元素是否存在
e, ok := m["giao"]
若"giao"在m中,则ok为true
若"giao"不在m中,则ok为false
从映射中读取某个不存在的键时,结果为映射的元素类型的零值
方法
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := Vertex{3, 4}
fmt.Println(v.Abs())
}
接口
隐式实现
type Greeter interface {
Greet() string
}
type EnglishGreeter struct {}
func (g *EnglishGreeter) Greet() string {
return "Hello"
}
空接口
var i interface {}
实现 Stringer 内建接口
type Stringer interface {
String() string
}
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}
func main() {
p := Person{"giao", 123}
println(p)
}
实现 Error 内建接口
type error interface {
Error() string
}
type GiaoError struct {
When time.Time
What string
}
func (e *GiaoError) Error() string {
return fmt.Sprintf("at %v, %s", e.When, e.What)
}
类型断言
t := i.(T)
如果 i 没有保存 T 类型的值,该语句会触发一个 panic
t, ok := i.(T)
如果 i 没有保存 T 类型的值,t 将为 T 类型的零值,ok 为 false
类型选择
func do(i interface{}) {
switch v := i.(type) {
case int:
fmt.Printf("Twice %v is %v\n", v, v*2)
case string:
fmt.Printf("%q is %v bytes long\n", v, len(v))
default:
fmt.Printf("I don't know about type %T!\n", v)
}
}
func main() {
do(21)
do("hello")
do(true)
}
Goroutine
go f(x)
信道
队列式数据结构
ch := make(chan int)
ch <- v // 将 v 发送至 ch 信道
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
fmt.Printf("Worker %d started job %d\n", id, j)
time.Sleep(time.Second)
fmt.Printf("Worker %d finished job %d\n", id, j)
results <- j * 2
}
}
func main() {
jobs := make(chan int, 100)
results := make(chan int, 100)
// 启动 3 个 worker
for w := 1; w <= 3; w++ {
go worker(w, jobs, results)
}
// 发送 9 个 jobs 到 jobs 通道中
for j := 1; j <= 9; j++ {
jobs <- j
}
close(jobs) // 通道关闭
// 读取所有结果
for a := 1; a <= 9; a++ {
<-results
}
}
信道容量为0时,该信道为无缓冲信道,在发送数据时,必须要求被立即接收
信道容量为1时,该信道只能存一个数据,若再往里发送数据,会造成阻塞,可以用来做锁
信道容量大于1时,该信道可以存放多个数据,可以作为多个协程之间的通信管道共享资源
select
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() { // 匿名 Goroutine
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
当select中的其他分支都没有准备好时,default分支会执行
为了在发送或接收时不产生阻塞(非阻塞通道操作),可以使用default分支
可用于超时处理
通道同步
func main() {
done := make(chan bool, 1)
go worker(done)
<- done // 程序将在这里阻塞
}
通道遍历
func main() {
queue := make(chan string, 2)
queue <- "one"
queue <- "two"
close(queue) // 关闭通道
// 如果没有关闭通道,循环将会一直阻塞执行,等待第三个值
for e := range queue {
fmt.Println(e)
}
}
定时器
func main() {
timer := time.NewTimer(time.Second * 2)
fmt.Println("start")
<- timer.C // 阻塞
fmt.Println("end")
}