Golang 快速参考手册
逐渐增加记录的自己学习内容。
Go之旅(英文)请先阅读A Tour of Go(日文)
以下内容也必须阅读:
实践Go语言
Go编程语言规范
在接下来的说明中,省略了main()的定义和其他冗长的部分。请根据上下文进行理解和补充。
下载
下载Go
你好世界
package main
import "fmt"
func main() {
fmt.Printf("Hello, world.\n")
}
编译
go build hello.go # コマンドhelloが作成される
执行
./hello
Hello, World.
编译和执行
go run hello.go // 実行ファイルをtmpディレクトリに生成して実行
Hello, World.
语言的特点 de
文字の大小区別する式(expression)と文(statement)区別あり式を要求する場所に文は書けない。メモリ管理ガーベージコレクション(mark&sweep)else ifelse if++, –あり。ただし後置のみ。文なので式に書けない。
版本确认
go version
评论
//
から改行までブロックコメント/*
から */
まで(ネスト不可)变量
foo
Bar
BAZ
是否以大写字母开头是有意义的(如果以大写字母开头,将被导出到包外)。
未被使用的变量声明会导致语法错误。如果函数的返回值中存在未使用的值,则可以将其赋值给”_”。独立的”_”(空标识符)不是一个变量。
_, err := foo()
// 定義
var x int
var x int, y int
var x, y int
var (
x int
y int
)
// 初期化子(initializer)
// 初期化子があれば型を省略できる
var x, y int = 1, 2
var x, y = 1, 2
// 関数の中なら := による変数定義もできる
x, y := 1, 2
一种选择是“定数”。
在编译时评估的仅限于字符(character)、字符串(string)、布尔(boolean)和数值(numeric)的选项。
const Pi = 3.14
在括号中的const声明中,每个常量被设置为与第一个表达式相同的值。
const (
A = 1
B
C = 2
)
fmt.Println(A) // => 1
fmt.Println(B) // => 1
fmt.Println(C) // => 2
列举
通过在声明中使用常数生成器 iota,可以生成从0开始的枚举值(int类型)。
const (
_ = iota // blank identifier を使えば、0 を破棄できる
A
B
C
)
fmt.Println(A) // => 1
fmt.Println(B) // => 2
fmt.Println(C) // => 3
当出现const声明时,iota将被重置为0。此外,每个常量表达式的iota值将递增。
const (
A = iota + iota // 0 + 0 (同じ式に二回現れても、iotaの値は同じ)
B // 1 + 1
C // 2 + 2
)
fmt.Println(A) // => 0
fmt.Println(B) // => 2
fmt.Println(C) // => 4
基本形式
bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // uint8 の別名
rune // int32 の別名
// Unicode のコードポイントを表す
float32 float64
complex64 complex128
获取变量的类型
import "reflect"
fmt.Println(reflect.TypeOf("foo")) // => string
// 表示だけなら、書式 %T も可
fmt.Printf("%T\n", "foo") // => string
零值 zhí)
没有设置初始值的变量将被以零值进行初始化。
范围
-
- パッケージ
-
- ファイル
-
- ブロック {}
if, for, switch
switch, select の case節
字面上的意思
1
070 // 8進数
0x10 // 16進数
1.0
true
false
"foo"
`bar\n` raw string ("bar\\n"と同じエスケープ文字の効果がない)
[]int{0,1,2}
map[string]int{"a": 1; "b": 2 }
nil // ポインタ、関数、インタフェース、スライス、チャネル、マップ型のゼロ値。スライスなら空の値(長さ0,容量 0)
演员阵容
x := uint8(10)
列表、切片
数组是一个分配了固定内存空间的区域
切片是对数组的部分引用
[n]T // 型表記: 型Tの配列。長さn
[]T // 型表記: 型Tの配列を参照できるスライス
var p []int = []int{0,1,2,3} // intの配列を生成しそのスライスを定義
p[2] => 2 // 要素参照
* 配列の範囲外参照はruntime error
* 負の数での参照もruntime error (即値で書けばコンパイルエラー)
len(p) // => 4 // 配列の長さ
cap(p) // => 4 // 配列の容量(capacity)=長さ
// スライス
a := [4]int{0,1,2,3} // 配列を生成
a[lo: hi] // 配列またはスライスa の lo から、hi-1までのスライスを返す
以下はいずれも配列と同じ長さのスライスを返し、同じ内容を参照する
a[0:len(p)]
a[0:]
a[:len(p)]
a[:]
// 配列aのインデックスnから長さlenのスライス
a[n: n+len]
a := [4]int{1,2,3,4}
s := a[0:2]
fmt.Println(s) // [1 2]
fmt.Println(len(s)) // => 2
fmt.Println(cap(s)) // => 4
// fmt.Println(s[2]) // => lenを超えた参照はcap内でもruntime error
// スライスは元の配列と同じ位置を指している
s[0] = 10
fmt.Println(a) // [10 2 3 4]
fmt.Println(s) // [10 2]
// aに別の配列を代入するとスライスも変わる(配列への代入は全要素のコピー、スライスへの代入は参照先の変更)
a = [4]int{2,4,6,8}
fmt.Println(a) // => [2 4 6 8]
fmt.Println(s) // => [2 4]
在上述的例子中,如果将数组类型[4]int(两处)转换为切片类型[]int,则最后的结果会发生变化(仍为[10 2])。在大多数情况下,最好使用切片类型[]int的变量,而不是使用明确的数组类型[4]int。
此外,使用[…]int{1,2,3,4}和”…”,可以在不明确长度的情况下书写数组类型(相当于写下数组的长度)。
v := [...]int{1,2,3,4}
fmt.Printf("%T\n", v) // => [4]int
a := [...]int{1,2,3,4}
s := a[0:3]
fmt.Println(a) // => [1 2 3 4]
fmt.Println(s) // => [1 2 3]
// スライスに要素を追加。cap内なら既存の要素が書き換わる
s = append(s, 5)
fmt.Println(a) // => [1 2 3 5]
fmt.Println(s) // => [1 2 3 5]
// スライスに要素を追加。capを超えると新たな配列が生成される
s = append(s, 6)
fmt.Println(a) // => [1 2 3 5]
fmt.Println(s) // => [1 2 3 5 6]
// 生成
s := make([]int, 5) // 中身は0で初期化
s := make([]int, 5, 100) capacity 指定(長さ100の配列を生成し、len=5、cap=100のスライスを返す)
cap(s) // => 100
// 拡張/縮小(re-slicing)
スライス演算で、cap以上の拡張はできない
s = s[:cap(b)]
地图
用其他语言表示关联数组、哈希表或字典
map[K]T // 型表記: 型Tを値、型Kをキーとするマップ
// 補足: map[K] は、T型であると読める。go では、型表記がC言語のように規則的であり、かつ逆向きのためC言語ほど複雑にならない。
// 生成
foo := make(map[キーの型]値の型)
foo := map[string]uint32 {
"a": 1,
"b": 2, // 最後の要素に余分なカンマを書ける
}
// 参照
foo["a"] // キーがなければ値の型のゼロを返す
// キーの有無確認は、2番目の戻り値で判断する
v, ok := foo["c"]
if ok == true {
// あり
}
// 置き換え
bar["b"] = 2
// 削除
delete(foo, "a")
运算符
// 論理演算子
"||"
"&&"
"!"
// 比較演算子
"==" | "!=" | "<" | "<=" | ">" | ">="
// 算術演算子
"+" | "-" | "*" | "/" | "%"
// ビット演算子
"|" | "^" | "&" | "&^"
"<<" | ">>"
// アドレス演算子
"&" | "*"
// 受信演算子(channelから値を取得)
"<-"
控制结构
叉路
如果有文章可以读
if i > 0 {
} else if i < 0 {
} else {
}
// else の前で改行するとエラー(以下はNG)
if i > 0 {
}
else {
}
// 変数定義付(スコープはifの条件式とブロックの中のみ)
if i := 0; i > 0 {
} else if i < 0 {
}
转换文本
在switch语句中,不需要使用break来退出一个case块。相反,如果要执行下一个case块,需要使用fallthrough。使用逗号来将多个case块合并在一起。在case块中可以使用任意的表达式作为值。case表达式会按照从上到下的顺序进行评估。
switch i := 0; i {
case 0:
case 1, 3:
fallthrough
default:
}
switch の式を省略するとtrueの意味になる。
switch {
case i == 0:
case i == 1 || i == 3:
fallthrough
default:
}
切换类型
在处理动态类型的分支选择中使用类型判断。
在类型判断语句中无法使用fallthrough。
// 型 interface{} は、任意の型を受け付ける(後述のinterfaceを参照)
func foo(v interface{}) {
switch t := v.(type) { // ".(type)" は常にこう書く
case nil:
fmt.Printf("nil: %#v %T\n", t, t)
case bool:
fmt.Printf("bool: %#v %T\n", t, t)
case int:
fmt.Printf("int: %#v %T\n", t, t)
default:
fmt.Printf("default: %#v %T\n", t, t)
}
}
foo(nil) // => nil: <nil> <nil>
foo(1) // => int: 1 int
foo("xx") // => default: "xx" string
循环
// for ループ
for i:= 0; i < 10; i++ {
}
// foreach ループ
// range キーワードを使う(range はforでのみ使用できる構文)
for i, v := range []int{1,2,3,4} {
fmt.Printf("%d, %d\n", i, v)
}
// while ループ
i := 0
for ; i < 10; {
i++
}
i := 0
for i < 10 {
i++
}
// 無限ループ
for {
}
函数 shù)
func(V)T // 型表記: 型Tの値を返す、型Vを引数に取る関数
func 関数名(引数) 戻り値の型 {
return x
}
可以返回多个返回值。
func 関数名(引数) (string, string) {
return x, y
}
如果在返回类型上写上变量名,就不需要写return的参数了(也可以写return的参数,那个值会被返回)。
func 関数名(引数) (x, y string) {
return
}
变量的定义重复,无法按照以下方式书写(会产生错误)。
func 関数名(x, y string) (x, y string) {
return
}
闭包
z := 2
foo := func(x, y int) int {
return x*y*z
}
for i, v := range []int{1,2,3,4} {
fmt.Printf("%d * %d * %d = %d\n", i, v, z, foo(i, v))
}
func foo() func()int {
z := 1
return func() int {
z++
return z
}
}
func main() {
bar := foo()
fmt.Println(bar()) // => 2
fmt.Println(bar()) // => 3
}
结构体
struct {T} // 型表記: 型Tの値をメンバーに持つ構造体
type 構造体名 struct {
X, Y int
}
// 定義(struct literal)
v := 構造体名{1, 2}
v := 構造体名{x: 1} // y 0
v := 構造体名{} // x, y => 0
// メンバ参照
v.X
指针
指针是指向C语言中变量的容器位置的类型,与对象的引用不同。没有箭头操作符(->)。
p := &v
p.X = 2
fmt.Println(v.X) # => 2
如果函数的参数不是通过指针传递,那么传递的将会是值的副本,不会对调用者产生影响。
type T struct {
X int
}
func foo(v T) {
v.X = 2
}
func main() {
v := T{1}
foo(v)
fmt.Println(v) // => {1}
}
在中国的本地化是“定义”。
foo(v *T)
召唤
foo(&v)
如果这样的话,会返回{2}。
新的内存分配
将初值为零的T分配给内存,并返回该指针。
var t *T = new(T)
または、
t := new(T) # t は、Tのポインタ型
make() 是一个特殊的内存分配函数,专用于分配引用类型的切片、映射和通道(返回分配的内部数据结构并作为引用返回)。
方法
// 定義
func (p *T)foo() int {
return p.X
}
// 呼び出し(vは、T型)
v.foo()
无法在包外的类型或基本类型中定义方法。
可以定义基于基本类型创建的类型的方法。
type MYINT int
func (p *MYINT)foo() int {
return p * 2
}
v := MYINT(1)
v.foo()
界面
定义了一系列方法的类型
interface {
Foo() int
Bar() int
}
具有相同方法的类型被认为是实现了该接口。
package main
import "fmt"
type I interface {
Foo() int
}
type T struct {
X int
}
func (p T) Foo() int { // (p *T)ではだめ
return p.X
}
func main() {
var v I
v = T{1} // interface I と同じメソッドFoo()を持つので代入可能
fmt.Println(v.Foo())
}
如果方法的接收器类型是指针,那么将地址赋值给接口类型的变量。
func (p *T) Foo() int { // (p *T)
return p.X
}
var v I = &T{1} // & でポインタを代入する
如果方法的接收器类型是值类型,则两种方式都可以。
func (p T) Foo() int { // (p *T)
return p.X
}
var v I = T{1} // 値も可能
var v I = &T{1} // ポインタも可能
所有类型都被认为是实现了空接口 interface {}。
var v interface{}
v = 1 // 何でも代入可能
var n int
n = v // 逆はNG
可以通过列举接口的类型名称来引用该接口,但是无法以递归的方式引用。
type I1 interface {
Foo() int
}
type I2 interface {
I1 // Foo() int と書いたのと同じ
Bar() int
}
并发
协程(go)
// goroutineを生成し関数foo()を並行に実行する
go foo(x, y, z)
// 無名の関数呼び出しを使えば、関数をあらかじめ作らずに並列処理を書くことができる
go func(x int) {
fmt.Println(x)
}(1)
频道
chan T // 型表記: T型を送受信するチャネル
// 生成
ch := make(chan int)
ch := make(chan int, 100) // バッファ数指定
// 送信
ch <- v //バッファがいっぱいならブロックする
// 受信
v := <- ch // データがなければブロックする
func foo(c chan int) { c <- 1 }
func bar(c chan int) { c <- 2 }
c := make(chan int)
go foo(c)
go bar(c)
x, y := <- c, <- c // x, y は、1, 2 または、2, 1 が返る
// chanは参照型のためポインタ渡しする必要はない
关闭频道
close(ch)
关闭由发送方执行。
在汉语中重新表述如下:倾听者通过第二个返回值来判断是否有输入(是否被关闭)。
v, ok := <-ch
if ok == true {
// ある
}
直到输入被关闭为止,循环继续。
for i := range ch {
}
原句:我喜欢去海边散步,感受海风和阳光。
Paraphrased: 我享受散步于海边,感受海风和阳光的滋润。
func foo(c chan int) {
for _, v := range []int{1,2,3} {
fmt.Println(v)
c <- v
}
close(c)
}
c := make(chan int)
go foo(c)
for i := range c {
fmt.Println(i * 10)
}
// 順番は保証されない
1
10
2
20
3
30
// 並列処理の完了待ち合わせを簡単に書くサンプル
package main
import "fmt"
func main() {
c := make(chan bool)
go func() {
// 何か長い処理
fmt.Println("goroutine")
c <- true // クロージャなので外側のchannelにアクセス可能
}()
// メインの処理
<- c // 待ち合わせ
fmt.Println("finish")
}
选择该文
同时等待来自多个通道的接收。只处理先到达数据的通道。
如果在执行select时已经收到多个数据,则随机处理其中一个case。
如果默认值被指定且数据未送达时,不会阻塞而是执行默认处理。
select {
case v <- ch1:
fmt.Println(v)
case v <- ch2:
fmt.Println(v)
default:
fmt.Println("nothing")
}
输入”文”
type是給型別取別名的文法。別名的型別與原本的型別是有區別的。
type X struct {
X int
}
func foo() struct{int} {
x := struct {int}{1}
y := X{1}
// return y はng、 return x はok
return y // cannot use y (type X) as type struct { int } in return argument
}
其他
时间
目前的时间
import "time"
time.Now()
睡觉 (Shuì
import "time"
time.Sleep(ns) // ns は、ナノ秒
time.Sleep(1*time.Second) // 1秒スリープ
文字串操作(分割、連接)
import . "strings"
s := []string{"abc", "def", "ghi"}
v := Join(s, ",")
fmt.Printf("%#v\n", v) // => "abc,def,ghi"
x := Split(v, ",")
fmt.Printf("%#v\n", x) // => []string{"abc", "def", "ghi"}
x = SplitN(v, ",", 2)
fmt.Printf("%#v\n", x) // => []string{"abc", "def,ghi"}