Go-CheatSheet

Go CheatSheet | Go 语法速览与实践清单
Go (a.k.a. Golang) is a statically-typed programming language first developed at Google. It is derived from C with additional features such as garbage collection, type safety, dynamic-typing capabilities, additional built-in types, and a large standard library.
可以前往这里下载Gopkg.toml Gopkg.lock vendor/
这三个文件
在这里,我们首先对于
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
包管理与模块机制
Package
早期
fmt
net/http
github.com/user
作为根路径。
// goworkdir/src/project1/utils/auth.go
package utils
func Test1() string {
return "Test1"
}
// goworkdir/src/project1/controllers/login.go
package controllers
import "project1/utils"
func Test2() string {
return utils.Test1()
}
// goworkdir/src/project1/main.go
package main
import (
"fmt"
"project1/controllers"
)
func main() {
fmt.Println(controllers.Test2())
}
mypkg_linux.go // only builds on linux systems
mypkg_windows_amd64.go // only builds on windows 64bit platforms
我们可以使用 dep ensure -add github.com/pkg/errors
添加依赖,运行之后,其会在
[[constraint]]
name = "github.com/pkg/errors"
version = "0.8.0"
简单的
package main
import "fmt"
func main() {
fmt.Println("hello world")
}
Go Modules
$ mkdir -p /tmp/scratchpad/hello
$ cd /tmp/scratchpad/hello
然后初始化所需要的模块
$ go mod init github.com/you/hello
go: creating new go.mod: module github.com/you/hello
然后照常编写
// hello.go
package main
import (
"fmt"
"rsc.io/quote"
)
func main() {
fmt.Println(quote.Hello())
}
在执行 go build
命令之后,即可以在 go.mod
文件中查看模块定义与显式的声明
$ cat go.mod
module github.com/you/hello
require rsc.io/quote v1.5.2
模块结构
模块是包含了
module my/thing
require (
one/thing v1.3.2
other/thing v2.5.0 // indirect
...
)
exclude (
bad/thing v0.7.3
)
replace (
src/thing 1.0.2 => dst/thing v1.1.0
)
需要注意的是,该文件中声明的依赖,并不会在模块的源代码中使用
外部依赖
模块依赖项会被下载并存储到 GOPATH/src/mod
目录中,直接后果就是废除了模块的组织名称。假设我们正在开发的项目依赖于

go mod tidy
来移除未被使用的依赖,使用 go mod vendor
可以生成独立的
初始化函数
各个包中默认首字母大写的函数作为其他包可见的导出函数,而小写函数则默认外部不可见的私有函数。_
引入的包仅调用初始化函数:
import (
mongo "mywebapp/libs/mongodb/db" // 对引入的模块重命名
_ "mywebapp/libs/mysql/db" // 使用空白下划线表示仅调用其初始化函数
)
初始化函数会于属性初始化之后被调用:
// sandbox.go
package main
import "fmt"
var _ int64 = s()
func init() {
fmt.Println("init in sandbox.go")
}
func s() int64 {
fmt.Println("calling s() in sandbox.go")
return 1
}
func main() {
fmt.Println("main")
}
// a.go
package main
import "fmt"
var _ int64 = a()
func init() {
fmt.Println("init in a.go")
}
func a() int64 {
fmt.Println("calling a() in a.go")
return 2
}
/* 输出结果
calling a() in a.go
calling s() in sandbox.go
init in a.go
init in sandbox.go
main
*/
而单个文件中也可以定义多个初始化函数:
package main
import "fmt"
func init() {
fmt.Println("init 1")
}
func init() {
fmt.Println("init 2")
}
func main() {
fmt.Println("main")
}
表达式与控制流
变量声明与赋值
作为强类型静态语言,
// 声明三个变量,皆为 bool 类型
var c, python, java bool
// 声明不同类型的变量,并且赋值
var i bool, j int = true, 2
// 复杂变量声明
var (
ToBe bool = false
MaxInt uint64 = 1<<64 - 1
z complex128 = cmplx.Sqrt(-5 + 12i)
)
// 短声明变量
c, python, java := true, false, "no!"
// 声明常量
const constant = "This is a constant"
在
m1 := map[string]int{
"a":1,
"b":2,
}
m2 := map[string]int{
"a":1,
"b":2,
}
fmt.Println(reflect.DeepEqual(m1, m2))
条件判断
// 基础形式
if x > 0 {
return x
} else {
return -x
}
// 条件判断之前添加自定义语句
if a := b + c; a < 42 {
return a
} else {
return a - 42
}
// 常用的类型判断
var val interface{}
val = "foo"
if str, ok := val.(string); ok {
fmt.Println(str)
}
Switch
// 基础格式
switch operatingSystem {
case "darwin":
fmt.Println("Mac OS Hipster")
// 默认 break,不需要显式声明
case "linux":
fmt.Println("Linux Geek")
default:
// Windows, BSD, ...
fmt.Println("Other")
}
// 类似于 if,可以在条件之前添加自定义语句
switch os := runtime.GOOS; os {
case "darwin": ...
}
// 使用 switch 语句进行类型判断:
switch v := anything.(type) {
case string:
fmt.Println(v)
case int32, int64:
...
default:
fmt.Println("unknown")
}
number := 42
switch {
case number < 42:
fmt.Println("Smaller")
case number == 42:
fmt.Println("Equal")
case number > 42:
fmt.Println("Greater")
}
或者进行多条件匹配:
var char byte = '?'
switch char {
case ' ', '?', '&', '=', '#', '+', '%':
fmt.Println("Should escape")
}
type T struct {
name string
}
t := T{}
switch (interface{})(t).(type) {
case T:
fmt.Print("1")
}
循环
for i := 1; i < 10; i++ {
}
// while - loop
for ; i < 10; {
}
// 单条件情况下可以忽略分号
for i < 10 {
}
// ~ while (true)
for {
}
我们也可以使用
// loop over an array/a slice
for i, e := range a {
// i 表示下标,e 表示元素
}
// 仅需要元素
for _, e := range a {
// e is the element
}
// 或者仅需要下标
for i := range a {
}
// 定时执行
for range time.Tick(time.Second) {
// do it once a sec
}
因为
// Reverse a
for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
a[i], a[j] = a[j], a[i]
}
Function | 函数
定义,参数与返回值
// 简单函数定义
func functionName() {}
// 含参函数定义
func functionName(param1 string, param2 int) {}
// 多个相同类型参数的函数定义
func functionName(param1, param2 int) {}
// 函数表达式定义
add := func(a, b int) int {
return a + b
}
func adder(args ...int) int {
total := 0
for _, v := range args { // Iterates over the arguments whatever the number.
total += v
}
return total
}
adder(1, 2, 3) // 6
adder(9, 9) // 18
nums := []int{10, 20, 30}
adder(nums...) // 60
// 传入任意类型的不定参数
我们也可以使用
func Filter(s []int, fn func(int) bool) []int {
var p []int // == nil
for _, v := range s {
if fn(v) {
p = append(p, v)
}
}
return p
}
// 返回单个值
func functionName() int {
return 42
}
// 返回多个值
func returnMulti() (int, string) {
return 42, "foobar"
}
var x, str = returnMulti()
// 命名返回多个值
func returnMulti2() (n int, s string) {
n = 42
s = "foobar"
// n and s will be returned
return
}
var x, str = returnMulti2()
虽然
func add(x, y int) int {
return x+ y
}
func adder(x int) (func(int) int) {
return func(y int) int {
return add(x, y)
}
}
func main() {
add3 := adder(3)
fmt.Println(add3(4)) // 7
}
闭包| Closure
func scope() func() int{
outer_var := 2
foo := func() int { return outer_var}
return foo
}
闭包中并不能够直接修改外层变量,而是会自动重定义新的变量值:
func outer() (func() int, int) {
outer_var := 2
inner := func() int {
outer_var += 99
return outer_var // => 101 (but outer_var is a newly redefined
}
return inner, outer_var // => 101, 2 (outer_var is still 2, not mutated by inner!)
}
函数执行
func read(...) (...) {
f, err := os.Open(file)
...
defer f.Close()
...
return .. // f will be closed
多个
package main
import "fmt"
func main(){
defer func(){
fmt.Println("1")
}()
defer func(){
fmt.Println("2")
}()
defer func(){
fmt.Println("3")
}()
}
异常处理
type error interface {
Error() string
}
某个可能返回异常的函数调用方式如下:
import (
"fmt"
"errors"
)
func main() {
result, err:= Divide(2,0)
if err != nil {
fmt.Println(err)
}else {
fmt.Println(result)
}
}
func Divide(value1 int,value2 int)(int, error) {
if(value2 == 0){
return 0, errors.New("value2 mustn't be zero")
}
return value1/value2 , nil
}
Panic 与Recover
_, err := os.Create("/tmp/file")
if err != nil {
panic(err)
}
当函数
package main
import "fmt"
func main(){
defer func(){
if r := recover();r != nil{
fmt.Println(r)
}
}()
panic([]int{12312})
}
数据类型与结构
interface{}
来表示任意类型。
类型绑定与初始化
// IntSlice 并不等价于 []int,但是可以利用类型转换进行转换
type IntSlice []int
a := IntSlice{1, 2}
可以使用
t := obj.(T) // if obj is not T, error
t, ok := obj.(T) // if obj is not T, ok = false
// 类型转换与判断
str, ok := val.(string);
基本数据类型
interface {} // ~ java Object
bool // true/false
string
int8 int16 int32 int64
int // =int32 on 32-bit, =int64 if 64-bit OS
uint8 uint16 uint32 uint64 uintptr
uint
byte // alias for uint8
rune // alias for int32, represents a Unicode code point
float32 float64
字符串
// 多行字符串声明
hellomsg := `
"Hello" in Chinese is 你好 ('Ni Hao')
"Hello" in Hindi is नमस्ते ('Namaste')
`
fmt.Println("Hello, 你好, नमस्ते, Привет, ᎣᏏᏲ") // basic print, plus newline
p := struct { X, Y int }{ 17, 2 }
fmt.Println( "My point:", p, "x coord=", p.X ) // print structs, ints, etc
s := fmt.Sprintln( "My point:", p, "x coord=", p.X ) // print to string variable
fmt.Printf("%d hex:%x bin:%b fp:%f sci:%e",17,17,17,17.0,17.0) // c-ish format
// Sprintf 能够仅进行字符串格式化,返回格式化后的字符串,而非输出到控制台
s2 := fmt.Sprintf( "%d %f", 17, 17.0 )
// 将其输出到错误流中
fmt.Fprintf(os.Stderr, "an %s\n", "error")
序列类型
Array
其中
[N]Type
[N]Type{value1, value2, ..., valueN}
// 由编译器自动计算数目
[...]Type{value1, value2, ..., valueN}
其具体使用方式为:
// 数组声明
var a [10]int
// 赋值
a[3] = 42
// 读取
i := a[3]
// 声明与初始化
var a = [2]int{1, 2}
a := [2]int{1, 2}
// 加 ... 会限制数组长度
a := [...]int{1, 2}
// 声明二维数组
array := [2][3]int{{1, 2, 3}, {4, 5, 6}}
var arr = [3]int{1, 2, 3}
arr := [...]int{1, 2, 3}
len(arr) // 3
cap(arr) // 3
不同于
Slice
// 使用内置函数创建
make([]Type, length, capacity)
make([]Type, length)
// 声明为不定长度数组
[]Type{}
[]Type{value1, value2, ..., valueN}
// 对现有数组进行切片转换
array[:]
array[:2]
array[2:]
array[2:3]
// 不定类型切片声明
a := []interface{}{2, 1, []interface{}{3, []interface{}{4, 5}, 6}, 7, []interface{}{8}}
// 二维不定类型切片
b := [][]interface{}{
[]interface{}{1, 2},
[]interface{}{3, 4},
}
不同于

// 创建 len 为 5,cap 为 5 的 Slice
s := make([]byte, 5)
// 对 Slice 进行二次切片,此时 len 为 2,cap 为 3
s = s[2:4]
// 恢复 Slice 的长度
s = s[:cap(s)]
需要注意的是,切片操作并不会真实地复制
d := []byte{'r', 'o', 'a', 'd'}
e := d[2:]
// e == []byte{'a', 'd'}
e[1] = 'm'
// e == []byte{'a', 'm'}
// d == []byte{'r', 'o', 'a', 'm'}
// len=0 cap=0 []
var s []int
// len=1 cap=2 [0]
s = append(s, 0)
// len=2 cap=2 [0 1]
s = append(s, 1)
// len=5 cap=8 [0 1 2 3 4]
s = append(s, 2, 3, 4)
// 使用 ... 来自动展开数组并进行合并
a := []string{"John", "Paul"}
b := []string{"George", "Ringo", "Pete"}
a = append(a, b...) // equivalent to "append(a, b[0], b[1], b[2])"
// a == []string{"John", "Paul", "George", "Ringo", "Pete"}
我们也可以使用内置的
func copy(dst, src []T) int
// 申请较大的空间容量
t := make([]byte, len(s), (cap(s)+1)*2)
copy(t, s)
s = t
Map | 映射类型
var m map[string]int
m = make(map[string]int)
m["key"] = 42
// 删除某个键
delete(m, "key")
// 测试该键对应的值是否存在
elem, has_value := m["key"]
// map literal
var m = map[string]Vertex{
"Bell Labs": {40.68433, -74.39967},
"Google": {37.42202, -122.08408},
}
Struct & Interface | 结构体与接口
Struct | 结构体
// 声明结构体
type Vertex struct {
// 结构体的属性,同样遵循大写导出,小写私有的原则
X, Y int
z bool
}
// 也可以声明匿名/隐式结构体
point := struct {
X, Y int
}{1, 2}
// 声明空指针
var v *Vertex = new(Vertex)
// 显式声明键
var v = Vertex{X: 1, Y: 2}
// 声明数组
var v = []Vertex{{1,2},{5,2},{5,5}}
// 创建结构体实例
var v = Vertex{1, 2}
// 读取或者设置属性
v.X = 4;
方法的声明也非常简洁,只需要在
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
// Call method
v.Abs()
对于那些需要修改当前结构体对象的方法,则需要传入指针:
func (v *Vertex) add(n float64) {
v.X += n
v.Y += n
}
new 与make
Pointer | 指针
// p 是 Vertex 类型
p := Vertex{1, 2}
// q 是指向 Vertex 的指针
q := &p
// r 同样是指向 Vertex 对象的指针
r := &Vertex{1, 2}
// 指向 Vertex 结构体对象的指针类型为 *Vertex
var s *Vertex = new(Vertex)
当我们在定义结构体时,可以使用指针或者值作为接受者来定义方法用指针作为接收者,那么变量(或者可以称作对象)本身是按引用传递的,在方法内可以修改对象的数据。使用值接收者,以为这是按值传递的,那么对象在方法内是处于只读状态的。并且指针类型时调用方法会复制
type VideoItem struct {
GroupId int64
ItemId int64
AggrType int32
}
func (item *VideoItem) TestPointer(GroupId int64) {
fmt.Printf("TestPointer %p %v\n", item, item)
item.GroupId = GroupId
}
func (item VideoItem) TestValue(GroupId int64) {
fmt.Printf("TestPointer %p %v\n", &item, &item)
item.GroupId = GroupId
}
func main() {
v := VideoItem{}
fmt.Printf("TestPointer %p %v\n", &v, &v)
// 值不变
v.TestValue(1)
// v 的 GroupId 被修改为 2
v.TestPointer(2)
// 值不变
(&v).TestValue(3)
// v 的 GroupId 被修改为 4
(&v).TestPointer(4)
fmt.Println(v)
}
/*
TestPointer 0xc420018300 &{0 0 0}
TestPointer 0xc420018360 &{0 0 0}
TestPointer 0xc420018300 &{0 0 0}
TestPointer 0xc4200183c0 &{0 0 0}
TestPointer 0xc420018300 &{0 0 0}
*/
传递普通变量传递值拷贝,不能修改原始值,如果是大对象则内存效率不高。
传递变量的指针,指针为固定大小,效率更高,可以就地修改对象的原始值。
在方法集的使用上,无论接收者是变量还是指针,都能直接正确调用,无需特殊处理,能正确调用所有绑定在该值或指针上的方法,
Interface | 接口
// 接口声明
type Awesomizer interface {
Awesomize() string
}
// 结构体并不需要显式实现接口
type Foo struct {}
// 而是通过实现所有接口规定的方法的方式,来实现接口
func (foo Foo) Awesomize() string {
return "Awesome!"
}
type Shape interface {
area() float64
}
func getArea(shape Shape) float64 {
return shape.area()
}
type Circle struct {
x,y,radius float64
}
type Rectangle struct {
width, height float64
}
func(circle Circle) area() float64 {
return math.Pi * circle.radius * circle.radius
}
func(rect Rectangle) area() float64 {
return rect.width * rect.height
}
func main() {
circle := Circle{x:0,y:0,radius:5}
rectangle := Rectangle {width:10, height:5}
fmt.Printf("Circle area: %f\n",getArea(circle))
fmt.Printf("Rectangle area: %f\n",getArea(rectangle))
}
//Circle area: 78.539816
//Rectangle area: 50.000000
惯用的思路是先定义接口,再定义实现,最后定义使用的方法:
package animals
type Animal interface {
Speaks() string
}
// implementation of Animal
type Dog struct{}
func (a Dog) Speaks() string { return "woof" }
/** 在需要的地方直接引用 **/
package circus
import "animals"
func Perform(a animal.Animal) { return a.Speaks() }
func funcName(a INTERFACETYPE) CONCRETETYPE
定义接口:
package animals
type Dog struct{}
func (a Dog) Speaks() string { return "woof" }
/** 在需要使用实现的地方定义接口 **/
package circus
type Speaker interface {
Speaks() string
}
func Perform(a Speaker) { return a.Speaks() }
Embedding | 嵌入
// ReadWriter 的实现需要同时满足 Reader 与 Writer
type ReadWriter interface {
Reader
Writer
}
// Server 暴露了所有 Logger 结构体的方法
type Server struct {
Host string
Port int
*log.Logger
}
// 初始化方式并未受影响
server := &Server{"localhost", 80, log.New(...)}
// 却可以直接调用内嵌结构体的方法,等价于 server.Logger.Log(...)
server.Log(...)
// 内嵌结构体的名词即是类型名
var logger *log.Logger = server.Logger
Reflection & Generics | 反射与泛型
Web 编程
HTTP Server
package main
import (
"fmt"
"net/http"
)
// define a type for the response
type Hello struct{}
// let that type implement the ServeHTTP method (defined in interface http.Handler)
func (h Hello) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello!")
}
func main() {
var h Hello
http.ListenAndServe("localhost:4000", h)
}
// Here's the method signature of http.ServeHTTP:
// type Handler interface {
// ServeHTTP(w http.ResponseWriter, r *http.Request)
// }
Beego
利用
quickstart
├── conf
│ └── app.conf
├── controllers
│ └── default.go
├── main.go
├── models
├── routers
│ └── router.go
├── static
│ ├── css
│ ├── img
│ └── js
├── tests
│ └── default_test.go
└── views
└── index.tpl
在
package main
import (
_ "quickstart/routers"
"github.com/astaxie/beego"
)
func main() {
beego.Run()
}
而在路由的初始化函数中,我们会声明各个路由与控制器之间的映射关系:
package routers
import (
"quickstart/controllers"
"github.com/astaxie/beego"
)
func init() {
beego.Router("/", &controllers.MainController{})
}
也可以手动指定
beego.SetStaticPath("/down1", "download1")
beego.SetStaticPath("/down2", "download2")
在具体的控制器中,可以设置返回数据,或者关联的模板名:
package controllers
import (
"github.com/astaxie/beego"
)
type MainController struct {
beego.Controller
}
func (this *MainController) Get() {
this.Data["Website"] = "beego.me"
this.Data["Email"] = "astaxie@gmail.com"
this.TplNames = "index.tpl" // version 1.6 use this.TplName = "index.tpl"
}
DevPractics | 开发实践
文件读写
import (
"io/ioutil"
)
...
datFile1, errFile1 := ioutil.ReadFile("file1")
if errFile1 != nil {
panic(errFile1)
}
...
测试
/** 交换函数 */
func swap(x *int, y *int) {
x, y = y, x
}
/** 自动生成的测试函数 */
func Test_swap(t *testing.T) {
type args struct {
x *int
y *int
}
tests := []struct {
name string
args args
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
swap(tt.args.x, tt.args.y)
})
}
}