var foo int// 无初值的声明 var foo int = 42// 带初值的声明 var foo, bar int = 42, 1302// 一次性声明并初始化多个变量 var foo = 42// 类型推断,由使用的上下文决定 foo := 42// 简短声明,只能用在函数内部 const constant = "This is a constant"
// 在函数的最后一个参数类型前,使用 ... 可表明函数还能接收 0 到多个此种类型的参数 // 下边的函数在调用时传多少个参数都可以 funcadder(args ...int)int { total := 0 for _, v := range args { // 使用迭代器逐个访问参数 total += v } return total }
funcmain() { // 一般的条件判断 if x > 0 { return x } else { return -x } // 在条件判断语句前可塞一条语句,使代码更简洁 if a := b + c; a < 42 { return a } else { return a - 42 } // 使用 if 做类型断言 var val interface{} val = "foo" if str, ok := val.(string); ok { fmt.Println(str) } }
Loops
1 2 3 4 5 6 7 8 9
// Go 语言中循环结构只有 for,没有 do、while、until、foreach 等等 for i := 1; i < 10; i++ { } for ; i < 10; { // 等效于 while 循环 } for i < 10 { // 只有一个判断条件时可省去分号 } for { // 无条件循环时,等效于 while(true) }
// switch 分支语句 switch operatingSystem { case"darwin": fmt.Println("Mac OS Hipster") // case 语句自带 break,想执行所有 case 需要手动 fallthrough case"linux": fmt.Println("Linux Geek") default: // Windows, BSD, ... fmt.Println("Other") }
// 和 if、for 语句一样,可在判断变量之前加入一条赋值语句 switch os := runtime.GOOS; os { case"darwin": ... }
// 在 switch 中还能做比较,相当于 switch (true) {...} number := 42 switch { case number < 42: fmt.Println("Smaller") case number == 42: fmt.Println("Equal") case number > 42: fmt.Println("Greater") }
var a [10]int// 声明长度为 10 的 int 型数组,注意数组类型 = (元素类型 int,元素个数 10) a[3] = 42// 设置元素值 i := a[3] // 读取元素值
// 声明并初始化数组 var a = [2]int{1, 2} a := [2]int{1, 2} // 简短声明 a := [...]int{1, 2} // 数组长度使用 ... 代替,编译器会自动计算元素个数
slices
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
var a []int// 声明 slice,相当于声明未指定长度的数组 var a = []int {1, 2, 3, 4} // 声明并初始化 slice (基于 {} 中给出的底层数组) a := []int{1, 2, 3, 4} // 简短声明 chars := []string{0:"a", 2:"c", 1: "b"} // ["a", "b", "c"]
var b = a[lo:hi] // 创建从 lo 到 hi-1 的 slice var b = a[1:4] // 创建从 1 到 3 的 slice var b = a[:3] // 缺省 start index 则默认为 0 var b = a[3:] // 缺省 end index 则默认为 len(a) a = append(a,17,3) // 向 slice a 中追加 17 和 3 c := append(a,b...) // 合并两个 slice
// 使用 make 创建 slice a = make([]byte, 5, 5) // 第一个参数是长度,第二个参数是容量 a = make([]byte, 5) // 容量参数是可选的
// 从数组创建 slice x := [3]string{"Лайка", "Белка", "Стрелка"} s := x[:] // slice s 指向底层数组 x
数组和 slice 的操作函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// 迭代数组或 slice for i, e := range a { // i 是索引 // e 是元素值 }
// 如果你只要值,可用 _ 来丢弃返回的索引 for _, e := range a { }
// 如果你只要索引 for i := range a { }
// 在 Go 1.4 以前的版本,如果 i 和 e 你都不用,直接 range 编译器会报错 forrange time.Tick(time.Second) { // 每隔 1s 执行一次 }
map
1 2 3 4 5 6 7 8 9 10 11 12 13 14
var m map[string]int m = make(map[string]int) m["key"] = 42 fmt.Println(m["key"])
delete(m, "key")
elem, ok := m["key"] // 检查 m 中是否键为 key 的元素,如果有 ok 才为 true
// 使用键值对的形式来初始化 map var m = map[string]Vertex{ "Bell Labs": {40.68433, -74.39967}, "Google": {37.42202, -122.08408}, }
结构体
Go 语言中没有 class 类的概念,取而代之的是 struct,struct 的方法对应到类的成员函数。
// 在从 channel 读数据的同时检测其是否已关闭 // 如果 ok 为 false,则 ch 已被关闭 v, ok := <-ch
// 从 channel 中读数据直到它被关闭 for i := range ch { fmt.Println(i) }
// select 语句中 任一 channel 不阻塞则自动执行对应的 case funcdoStuff(channelOut, channelIn chanint) { select { case channelOut <- 42: fmt.Println("We could write to channelOut!") case x := <- channelIn: fmt.Println("We could read from channelIn") case <-time.After(time.Second * 1): fmt.Println("timeout") } }
channel 开发原则
向 nil channel 写数据将卡死,一直阻塞
从 nil channel 读数据将卡死,一直阻塞
向已关闭的 channel 写数据将造成 panic
1 2 3 4 5 6 7 8
package main
funcmain() { var c = make(chanstring, 1) c <- "Hello, World!" close(c) c <- "Hello, Panic!" }
运行:
从已关闭的 channel 读数据将返回零值
1 2 3 4 5 6 7 8 9 10 11
package main
funcmain() { var c = make(chanint, 2) c <- 1 c <- 2 close(c) for i := 0; i < 3; i++ { println(<-c) } }
运行:
输出
1 2 3 4 5 6 7 8 9 10 11 12 13
fmt.Println("Hello, 你好, नमस्ते, Привет, ᎣᏏᏲ") // 最基本的输出,会自动加一个换行 p := struct { X, Y int }{ 17, 2 } fmt.Println( "My point:", p, "x coord=", p.X ) // 输出结构体字段等 s := fmt.Sprintln( "My point:", p, "x coord=", p.X ) // 组合字符串并返回