语言特点: 高并发,语法简洁,标准库丰富,静态链接,快速编译,跨平台,自带 GC。
Hello world:
package main
import "fmt"
func main() {
fmt.Println("Hello world!")
}
基本类型
Boolean: bool
Numeric:
uint8 uint16 uint32 uint64int8 int16 int32 int64float32 float64IEEE-754 32/64 位浮点数complex64 complex128(float32实部,float32虚部) / (float64实部,float64虚部)byte: uint8 的别名rune: int32 的别名,表示一个 Unicode 码点- implementation-specific:
int, uint32 or 64 bitsuintptr大小刚好能够存放一个指针
String:
stringlen(字符串)返回的是永不为负的字节数string是不可变的,一旦被创建就不能被修改- 如果是字符是编译期常数,那么在这个
len(字符串)也是一个编译期常数 - 使用
字符串[index]返回的是原始字节,并且无法取这个表达式的地址
Array:
[N]T:长度为 N 的 T 类型数组,数组类型始终是一维的,但是可以组合成多维数组
// 数组声明
arr := [...]int{1, 2, 3}
arr1 := [3]int{1, 2, 3}
arr2 := [3]int{}
// len(arr) == len(arr1) == len(arr2) == 3
arr[0] = 1
Slice:
[]T:长度可变的 T 类型切片slice一旦被创建,就会一直与原数组保持关联- 如果使用
make:make([]T, len, cap),cap可选。这样创建一个长度为len的数组并让slice指向它
// 切片声明
slice := []int{1, 2, 3}
slice1 := make([]int, 3, 10) // with cap = 10
array := [...]int{1, 2, 3, 4, 5}
slice2 := array[0:2] // slice2 = [1, 2],从0开始长度为2-0,也就是[0,2的左闭右开
// 方便起见
// arr[a:] 等同于 arr[a : len(a)]
// arr[:a] 等同于 arr[0 : a]
// arr[:] 等同于 arr[0 : len(a)]
slice3 := arr[1:3:5] // 1可以省略
// slice3 = [2, 3], len(slice3) == 3 - 1, cap(slice3) == 5 - 1
slice1 = append(slice1, 1) // append slice1 with 1
Pointer:
*T:T 类型指针,未初始化的指针为nil
// 指针声明
type MyInt = int
i := MyInt(1)
var p *int
p = &i
*p = 1
Structure:
struct{ ... }:结构体类型,接下里是几个例子
struct {
T1 // embedded field
_ T2 // padding
a, b int
}
// 非法
struct {
T \\ 与其他字段同名
*T \\ 同上
*P.T \\ 同上
}
struct {
a, b int "任何字符串都可以写在tag里" // tag可以用reflect包来获取,用于区别struct中的字段
}
使用方式:
// 声明
type Bar = struct {
a int
}
type Foo struct {
a int `Say something`
b int
Bar
}
func (*Foo) Greet() {
fmt.Println("Hello there")
}
k := Foo{1, 2, Bar{2}};
println(k.Bar.a)
k.Greet()
Function:
- 形如
func(T...) T...:函数类型,例子
// 最后一个参数的类型可以有 `...` 前缀,叫做variadic,可以接受任意个该类型的参数
func(a, b int, _ float32, res ...int) (success bool, result int)
func(x int) int
func(n int) func(p *T)
使用方式:
func foo(a, b int, _ float32, res ...int) (success bool, result int) {
if(len(res) == 1) {
return false, 2333
}
success = true
result = 42
return
}
foo(1, 2, 3.0, 4)
Interface:
interface{ ... }:接口类型,例子
// a simple file interface
interface {
Read([]byte) (int, error)
Write([]byte) (int, error)
Close() error
}
// 其中每一个字段名必须是唯一的
interface {
String() string
String() string // illegal: String not unique
_(x int) // illegal: method must have non-blank name
}
- 一个接口定义了一个类型集(Type set),任何类型都可以实现多个不同的接口,比如:所有类型都实现了空接口
interface{},空接口这个类型集包含了所有非接口类型。方便起见,interface{}被定义为any - Embedded Interface:
type A interface {
a()
foo()
}
type B interface {
b()
foo()
}
type C interface {
A
B
}
// C接口包含了A和B的所有方法,实现C接口的类型必须同时实现了A接口和B接口
// 换句话说,C类型集是A类型集和B类型集的交集(intersection)
General interface (泛型接口)
- underlying type:
- 每一个
T都有一个 underlying type,如果T被声明为是boolean,numeric, string或者是类型字面量,那么T的 underlying type 就是T本身,否则T的 underlying type 就是就是T类型被定义的类型的 underlying type。对于泛型函数参数 T 来说,underlying type 就是 T 的类型约束。
- 每一个
- 在泛型接口中,接口中的元素可能是任意类型
T,或者任何 underlying type 是 T 的类型~T,亦或者是几个类型集的并集T1 | T2 | ... | Tn- 空接口:是所有非接口类型的集合
- 非空接口:是其接口元素所代表的类型集的交集
- 函数声明:包括了所有包含了该函数声明的非接口类型的集合
- 非接口类型:是其自身
- 形如
~T:是所有 underlying type 是 T 的类型的集合 - 形如
T1 | T2 | ... | Tn:是所有T1, T2, ..., Tn的类型集的并集
例子:
// 只有int的类型集
interface {
int
}
// 个人对~T的理解就是:包括了T和所有别名是T和被定义为T的类型
// 也就是T, type Foo T 和 type Bar = T
// 一个代表所有underlying type是int的类型集
interface {
~int
}
// 一个包含所有underlying type是int和string的类型集的并集
interface {
~int
String() string
}
// 空的类型集,因为没有哪个类型同时是int和string
interface {
int
string
}
- 对于
~T,underlying type of T 必须是它本身,并且 T 不是接口 - 对于
T1 | T2 ... | Tn中的非接口类型集必须互不相交(相交为空集),例如~int | MyInt就是非法的(type MyInt int),因为 underlying type ofMyInt是int,但是float | Float则是合法的(type Float interface { float32 }),因为Float是一个接口。 - 非基本接口只能用作类型约束,不能用作类型声明。
- 类型 T 实现了接口 I 如果:
- T 不是接口并且 T 属于 I 类型集,或者
- T 是一个接口并且 T 类型集 I 的子集
Map:
map[K]V的哈希表,其中类型K之间的==和!=必须被定义,而且K不能是function,map,slice类型。- 创建 map 可以用
make,也可以用map字面量
// 创建一个空的map
make(map[string]int)
make(map[string]int, 100) // 100作为map初始容量的提示,可选
// 创建一个map并初始化
map[string]int{"one": 1, "two": 2}
Channel(FIFO):
chan T, chan <- T, <- chan T,表示了 channel 类型- 符号
<-是可选的,表示了是仅发送还是仅接收还是两者都有 - 使用 make 创建 channel
// cap为0或者写表示为无缓冲的channel,并在发送者和接收者同时准保好了之后才开始工作。
make(chan int)
make(chan int, 100) // 100为channel的缓冲区大小。
close来关闭 channel,多个返回值的接收运算符表示了在 channel 关闭前是否收到了新值。
变量声明:
// declaration
var a int
var a, b int = 1, 2
var (
a int
b int
)
var a, _, c = foo()
// short declaration
a, b := 1, 2
f := func() (int, int) { return 42, 1 }
a, _ := f()
if 语句:
if a := f(); a < b {
return a
} else if x > z {
return z
} else {
return b
}
switch 语句:
对于每一个 case 都是隐式 none fallthrough,如果需要继续执行,使用 fallthrough 语句
switch tag := 1; tag {
default:
fmt.Println("default")
case 0, 1:
fmt.Println("0 or 1")
fallthrough
case 2, 3:
fmt.Println("2 or 3")
case 4:
}
// output:
// 0 or 1
// 2 or 3
switch {
case x < y:
f1()
case x < z:
f2()
case x == 4:
f3()
}
for 语句:
// with single condition
for a < b {
a += 1
}
// with for clause
// 基本格式:for init; condition; post { ... }
// 其中 init, condition, post都是可选的,格式类似与C语言
// init 可以是short variable declaration,但是post一定不是
for i := 0; i < 10; i++ {
fmt.Println(i)
}
其中
for cond { S() } 等同于 for ; cond ; { S() }
for { S() } 等同于 for true { S() }
// with range clause
// 基本格式: for 变量名 = range 表达式 { ... }
// 或者 for 变量名 := range 表达式 { ... }
// 表达式可以内置的数组,切片,字符串,map,管道
其中
for i, _ := range foo { ... } 等同于 for i := range foo { ... }
对于 range 中不同的类型表达式:
| 表达式 | 1st 值 | 2nd 值 |
|---|---|---|
| 切片或者数组 | index: int | value |
| 字符串 | index: int | rune |
| map | key: K | value: V |
| channel | element: E |
- 对于字符串:如果遍历过程中遇到了无效的 UTF-8 序列,第二个返回值将会是 Unicode 替换字符(0xFFFD),并且下一次迭代将会跳过这个无效的字节,直接从下一个字节开始迭代。
- 对于管道:如果管道是 nil,那么 range 将会永久阻塞。