go基础
go语法与基本命令
go语法
基本上是A Tour of Go的笔记
package
每个 Go 程序都是由包构成的。
程序从 main
包开始运行。
只有
package main
中的func main
可以运行一个目录下只能有一个
package
,有不同的package
会报错同一个目录下存在多个
main
方法会报错
import
package main
import (
"fmt"
// alias
alias "math/rand"
// load for side-effect
_ "github.com/go-sql-driver/mysql"
)
func main() {
fmt.Println("My favorite number is", alias.Intn(10))
}
- package名可以和文件名或者目录名不一样
相同包内变量不用import
example
- 文件结构
> tree module
module
├── another.go
└── main.go
- 文件内容
// file module/main.go
package main
func main() {
println(plus(1, 2))
}
func add(a, b int) int {
return a + b
}
// file module/another.go
package main
func plus(a, b int) int {
return add(a, b)
}
- 大写的变量会暴露给外界
数组
var array []int
a := []int{1,2,3,4,5}
s := []string{"am", "is", "are"}
- 数组切片,和python类似
array[begin, end]
- 切片相当于view
- 范围[begin, end)
- 和python不同的是:end不能是负数,begin不能超过数组长度
general
type T int
var arrayT []T
arrayT = []T{a T, b T, c T}
切片
切片拥有 长度 和 容量。
切片的长度就是它所包含的元素个数。
切片的容量是从它的第一个元素开始数,到其底层数组元素末尾的个数。
切片 s
的长度和容量可通过表达式 len(s)
和 cap(s)
来获取。
var s []int
切片的零值是 nil
。
nil 切片的长度和容量为 0 且没有底层数组。
从头切片不会影响底层数组长度
不从头切片会影响底层数组长度
a := make([]int, 5, 5) // 从头切片不会影响底层数组长度 b := a[:3] println(len(b), cap(b)) // 3 5 // 不从头切片会新建底层数组(?) c := a[1:3] println(len(b), cap(b)) // 2 4
make
用于给slice或map预分配空间
创建切片
l := 5 // len
c := 10 // caps
a := make([]int, l, c)
创建map
m := make(map[string]int, 5)
println(len(m)) //5
append
向切片追加元素
func append(s []T, vs ...T) []T
append
的第一个参数 s
是一个元素类型为 T
的切片,其余类型为 T
的值将会追加到该切片的末尾。
append
的结果是一个包含原切片所有元素加上新添加元素的切片。
当 s
的底层数组太小,不足以容纳所有给定的值时,它就会分配一个更大的数组。返回的切片会指向这个新分配的数组。
map
// key: string
// value: int
var m map[string]int
- 未初始化时值为
nil
,无法添加key - map没有切片,固只有长度,没有容量
cap(m)
会报错
- 会自动扩容,可以用
make
预分配大小
m := map[string]int{
"a": 1,
"b": 2,
}
println(len(m)) // 2
m = make(map[string]int, 0)
println(len(m)) // 0
m["c"] = 123
println(len(m)) // 1
删除元素
delete(m, key)
检测元素是否存在
elem, ok := m[key]
// ok为true -> 存在
// 否则elem = nil
循环
for i:=0; i < 10; i ++ {
// expresion
}
while等价
i := 10
for i < 10 {
// expresion
}
死循环
for {}
range
for
循环的 range
形式可遍历切片或映射。
当使用 for
循环遍历切片时,每次迭代都会返回两个值。第一个值为当前元素的下标,第二个值为该下标所对应元素的一份副本。
for idx, value := range pow
// 只需要索引的语法糖
for idx := range pow
switch
func main() {
fmt.Print("Go runs on ")
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X.")
case "linux":
fmt.Println("Linux.")
default:
// freebsd, openbsd,
// plan9, windows...
fmt.Printf("%s.\n", os)
}
}
不会fallthrough
defer
外层函数结束后会调用该函数
defer file.Close()
defer栈
推迟的函数调用会被压入一个栈中。当外层函数返回时,被推迟的函数会按照后进先出的顺序调用。
func main() {
fmt.Println("counting")
for i := 0; i < 10; i++ {
defer fmt.Println(i)
}
fmt.Println("done")
}
- 上面代码会输出9 - 0
指针
指针保存值的地址
类型 *T
是指向 T
类型值的指针。其零值为 nil
。
var p *int
&
操作符会生成一个指向其操作数的指针。
i := 42
p = &i
*
操作符表示指针指向的底层值。
fmt.Println(*p) // 通过指针 p 读取 i
*p = 21 // 通过指针 p 设置 i
函数
func funcName(params Type) returnType {
// body
}
类型
f := func(i, j int) func(int) int {
return func(num int) int {
return num*i + num/j
}
}
fmt.Printf("%T\n", f)
//func(int, int) func(int) int
结构体
//定义
type MyStruct struct {
field1 int
field2 string
Field3 string // 包外可见
}
//赋值
var myStruct MyStruct
myStruct = MyStruct{
field1 1,
field2 "hello"
Field3 "exported"
}
//结构体指针
structPtr := &myStruct
//访问结构体字段
myStruct.field1
- 小写的字段包外不可见
结构体方法
带接受者参数的函数
func (r Receiver) fn(param T) rType {
//body
}
example
type MyStruct struct {
X int
Y int
z int // unexported
}
func (myStruct MyStruct) sum() {
return myStruct.X + myStruct.Y + myStruct.z
}
无法对包外的结构体类型赋予方法
你只能为在同一包内定义的类型的接收者声明方法,而不能为其它包内定义的类型(包括
int
之类的内建类型)的接收者声明方法。(译注:就是接收者的类型定义和方法声明必须在同一包内;不能为内建类型声明方法。)
在myS包中定义结构体
package myS
type MyStruct struct {
X int
Y int
z int // unexported
}
尝试在main
包中对myS.MyStruct
赋予方法
package main
import (
"myS"
"fmt"
)
// Cannot define new methods on the non-local type 'myS.MyStruct'
//func (s myS.MyStruct) sum() int
// 新建类别来赋予method
type MyStruct myS.MyStruct
func (myStruct MyStruct) sum() int {
return myStruct.X + myStruct.Y
}
func main() {
nonlocal := myS.MyStruct{
X: 0,
Y: 0,
}
local := MyStruct{
X: 1,
Y: 3,
}
fmt.Printf("%#v\n", nonlocal) //myS.MyStruct{X:0, Y:0, z:0}
fmt.Printf("%#v\n", local) //main.MyStruct{X:1, Y:3, z:0}
// nonlocal.sum() 无法调用
println(local.sum())
}
指针接收者
接收者为指针,则传递的是引用而不是值的副本
type MyStruct struct {
X int
Y int
}
func (my MyStruct) changeX(i int) {
my.X = i
}
func (my *MyStruct) changeXptr(i int) {
my.X = i // 是(*my).X = i 的语法糖
}
func main() {
my := MyStruct{
1, 2
}
my.changeX(3) // 不影响my
println(my.X) // 1
my.changeXptr(3) //相当于(&my).changeXptr(3) 语法糖
// my.X改变
println(my.X) // 3
}
go中方法语法糖
- 以指针为接收者的方法被调用时,接收者既能为值又能为指针
- 以值为接收者的方法被调用时,接收者既能为值又能为指针
选择值或指针作为接收者
使用指针接收者的原因有二:
首先,方法能够修改其接收者指向的值。
其次,这样可以避免在每次调用方法时复制该值。若值的类型为大型结构体时,这样做会更加高效。
在本例中,
Scale
和Abs
接收者的类型为*Vertex
,即便Abs
并不需要修改其接收者。通常来说,所有给定类型的方法都应该有值或指针接收者,但并不应该二者混用。
接口
接口类型定义一组方法签名,其类型变可以保存任何实现了这些方法的值
type Abser interface {
Abs() float64
}
type Vertex struct {
X, Y float64
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
var abser Abser
v := Vertex {1.23,3.14}
a = *v // *v实现了Abs(),固是Abser类型
// 但是v本身没有实现Abs(),固不是Abser类型
// a = v 报错
}
go中接口是隐式实现的,没有类似java中implements关键字
感觉有点像鸭子类型
interface{}
空接口可以保存任何值
接口使用实例,go中的”toString()”
type Stringer interface {
String() string
}
类型断言
判断某个值是否是类型T,并生成转换后新值
反射
t := i.(T)
example
var i interface{} = "hello"
s, ok := i.(string)
type-switch
switch v := i.(type) {
case T:
// v 的类型为 T
case S:
// v 的类型为 S
default:
// 没有匹配,v 与 i 的类型相同
}
i.(type)
只用用于type-switch,无法作为表达式- 类型不是一等公民
Error
类型定义
type error interface {
Error() string
}
泛型
怎么都2022年了,还有语言刚支持泛型啊?
类型参数
func Index[T comparable](s []T, x T) int
generics
package main
// List represents a singly-linked list that holds
// values of any type.
type List[T any] struct {
next *List[T]
val T
}
func main() {
}
go命令
使用方法
go <command> [arguments]
命令列表
bug start a bug report
build compile packages and dependencies
clean remove object files and cached files
doc show documentation for package or symbol
env print Go environment information
fix update packages to use new APIs
fmt gofmt (reformat) package sources
generate generate Go files by processing source
get add dependencies to current module and install them
install compile and install packages and dependencies
list list packages or modules
mod module maintenance
work workspace maintenance
run compile and run Go program
test test packages
tool run specified go tool
version print Go version
vet report likely mistakes in packages
使用案例
创建go.mod
文件
go mod init [module-path]
下载并添加依赖到当前模块
go get <module-name>
添加未添加的依赖到go.mod
文件/移除没有使用的依赖
go mod tidy
- 比如在代码中引入了
gorm.io/gorm
,但没有将其添加到go.mod
中 - 或者
go.mod
中存在没有使用的依赖
安装依赖时,遇事不决请使用
go mod tidy
执行文件
go run
配置国内代理
$ go env -w GO111MODULE=on
$ go env -w GOPROXY=https://goproxy.cn,direct
- go 1.13以上