写在前面
最近面试过程中有被问到go语言相关的内容, 正好把知识库中的go语言基础(含易错点)整理处理了
由于是自己知识库中的笔记, 内容相当简略
(目标用户是自己-_-), 各部分内容想参考详细内容的, 可以参考下面的资料
官网文档
- go.dev=golang.google.cn=golang.org
- https://go.dev/doc : EffectiveGo pkg cmd spec MemoryModel.sync releaseHistory
- https://go.dev/ref/spec#The_zero_value
- https://go.dev/ref/spec#Channel_types
社区中有不少优秀的资料
- go语言基础 -> go程序设计 2022.7
- go源码 -> go语言设计与实现 2021.11
- 如果需要一份golang面经 -> go程序员笔试面试宝典 2022.4
- The Go Programming Language Go程序设计语言(简称 gopl, Go语言圣经) 2015.10
英文版.强烈推荐
中文版
如果对阅读源码感兴趣的小伙伴, 推荐一个AI工具: deepwiki - go mutex实现原理
go语言基础
- type: decalre trans alias assert switch; 值类型vs指针类型; 0值可用
- bool number(int/uint byte=uint8 rune=int32 float complex) string array
- rune:
utf8
1-4 bytes -> rune=int32len("汉") utf8.RuneCountInString(string([]byte{0xE6, 0xB1, 0x89}))
- byte:
bytes
- string
strings
- fori.byte forr.rune string<->byte sting<->rune
-
+ +=
fmt
strings
- Trim=TrimLeft+TrimRight.存在就移除 TrimPrefix.TrimSuffix.完全匹配 strings.Builder使用Grow()预分配内存.底层是slice
- string 和 []byte 转换会分配内存 -> []byte直接使用byte包的方法
strings.Clone
.标准库中转string
- slice
make([]int, 0, cap) + append
- 切片的切片vs动态扩容
- nil slice 的几个特点: 不分配内存.函数返回值; marshal
nil slice -> null; empty slice ->[]
nil <> empty
- copy: 数量
min(len(dst), len(min))
- 不能并发append -> DataRace
- map
- 未初始化 map 无法使用:
var m map[int]int
(m map[int]int)
-> 使用 make 初始化 -
map[int]*T.A
没有进行 ok 判断:runtime error: invalid memory address or nil pointer dereference
- map 乱序: 数据查出来 slice -> 使用 map 拼接数据 -> 返回 map 的 values
sync.Map
- 初始化时指定长度; 底层bucket只增不降.delete也不行; 不要边遍历边写key.新写的key不一定会遍历出来
type set map[k]struct{}
- 未初始化 map 无法使用:
- struct
-
struct{} unsafe.Sizeof
嵌入字段.EmbeddedField 内存对齐.aligned -
&T{Field: value}
NewT()
&T{}=new(T)
- 初始化
var t T
-> 传递.函数入参返回.*T
- 内嵌接口.显式实现接口
-
- for
- for+go: 外部变量/传参
- forr:
v
是同一个地址不同值
- switch.注意和其他语言区别: break只推出当前switch.可省略
x.(type)
fallthrough - break + for/select/switch 只能跳出一层循环 -> break label
- func
-
func value with state
_ init() 包级别变量
new()
make() slice/map/chan
append() cap扩容
// NOTE: ignoring errors
- 传参: string/slice/map.描述符.浅拷贝
- 变长参数
func(a ...int)
=a []int
- NamedReturnValue.具名返回值
(a int)
=var a int
增加可读性.如经纬度 自动初始化0值 方法内return保持一致.长方法一律return a,b
-
var xxx func()
=func xxx()
- 函数类型
type HandlerFunc func(ResponseWriter, *Request)
->http.HandlerFunc(hello)
-
- err
- init:
errors.New fmt.Errorf
->if err!=nil {return err}
- wrap:
fmt.Errorf("xxx: %w", err)
- value.
ErrXxx
errors.Is(err2, ErrSentinel)
- type.
XxxError
net.OpError http.isCommonNetReadError
errors.As(err2, &e)
- interface.
net.Error
- interface.
- value.
-
pkg/errors %v %+v
errors.Wrap()
- 上生产的代码务必不要忽略错误
- 预期内err.使用value.
ErrFoo=errors.New("foo")
.errors.Is
非预期err.使用type.type BarError struct{}
.errors.As
- %w.wrap %v.转换
- init:
- panic/recover
- 运行时产生/panic()
- recover只在defer中有效; panic允许在defer中多次调用; panic只对当前goroutine的defer有效
- panic 会一直出栈, 直到进程退出或 recover -> recover必须要写到defer中 -> recover必须包一层.检查recover返回值
- 野生go.任何go中panic都会导致程序退出 ->
go func(){}
中必须捕获panichttp/server.go conn.serve
- 提示潜在bug.类似assert:
json/encode.go resolve()
- defer
- 性能go1.17; 参数与外部参数
- 场景:
defer mu.Unlock()
defer conn.Close()
- method.方法
- composition, not inheritance; associated with user-defined type
- 本质.receiver作为第一个参数的函数:
func (t *T) xxx(xxx)
=func xxx(t *T, xxx)
-
*T
: 需要修改 T; 含有不能复制的fieldsync.Mutex
不宜复制的field[]byte
; 比较占内存;T *T
的方法都可以使用->隐式指针转换; 实现interface的方法集合
- interface
- type+value
- 小接口.1-3个方法=抽象程度高+易于实现和测试+单一职责易于组合复用 组合.正交.垂直(嵌入)+水平
- 接口in.结构体out
Go 社区流传一个经验法则:“接受接口,返回结构体(Accept interfaces, return structs)
- 尽量不使用any
Go 语言之父 Rob Pike 曾说过:空接口不提供任何信息(The empty interface says nothing)
- go
- go
- chan
- select: case必须为chan; 无满足条件case->panic; 多个满足case->随机
- context
- 取消: cancel() deadline -> Done()返回的chan被关闭 Err()为什么取消
- key:
type key string + const Xxx key = "key"
- 并发问题.
etcd blankContext
- sync
- WaitGroup: 父协程Add, 子协程Done
- Cond.不太常用
- errgroup
- time.After: 创建一个chan.只在过期时释放
- sql.Open: 有些driver并不会连接数据库.
db.Ping()
- http
- resp: err为空就能Close
- timeout: client server
- Reader: API设计的好处.
[]byte
可由调用者决定使用堆/栈 - package: 大小写可见性 main.main() Visible Encapsulation pkg/local
- 进阶
- Arenas.手动管理内存 ortuman/nuke.民间库
- project: toolChain mod.pacakge dir.internal
- devops: pprof GOGC GOMAXPROCS
go源码
直接点进去看go源码+阅读源码解读的文章
- builtin 关键字: compareable append
- runtime
- slice mstats.有内存对齐的例子
- map
- chan.有锁队列
- runtime2: iface.eface g.p.m
- debug: SetCrashOutput
- cmd
go tool xxx
- compile:
WalkXxx
- compile:
- sync
- Mutex RWMutex Cond:
MustNotCopied
- Mutex RWMutex Cond:
- encoding
- json:
json:"user_id,omitempty,string"
json:",default=pro,options=dev|test|rt|pre|pro,optional,range=[0:10]"
- json:
- net
- http
- src: math container(list) sort fmt(v默认 +v结构体 #v语法表示 T类型) flag log io(os/bufio) path text/template html/template image mime hash crypto archive compress datebase time net reflect unsafe runtime
- 一定要使用:
time.Time time.Duration
- net 包和 net/http 包并没有层级关系
-
html/template: 会转义, 一定要注意使用场景
http_client_timeout
- 一定要使用:
gopl
preface: the origin of go; the go project; book organization; more info
program structure: name declar var assign type package&file scope
basic type: int float complex bool string const
composite type: arr slice map struct json text&html
func: delcar recursion multi-return err func-var anonymous variadic(多参数) defer panic recover
method: declare pointer-receiver struct-embed value&expr
interface: contract&satisfy type&value typeAsset typeSwitch advice
go&chan: go chan.buffered select.multiplex cancel.broadcast.close
co&shareMem: raceCondition mutex rwmutex once.lazyInit raceDetector go&thread
pkg&tool: import
_
naming GOPATH internalPackage get/build/doc/listtest: test cover bench prof
reflection: Type&Value Display stuctFieldTag method caution
lowLevel: unsafe cgo Caution
正如Rob Pike所说,“软件的复杂性是乘法级相关的”,通过增加一个部分的复杂性来修复问题通常将慢慢地增加其他部分的复杂性。通过增加功能、选项和配置是修复问题的最快的途径,但是这很容易让人忘记简洁的内涵,即从长远来看,简洁依然是好软件的关键因素。
一个包由位于单个目录下的一个或多个.go源代码文件组成, 目录定义包的作用。每个源文件都以一条package声明语句开始
在main里的main 函数 也很特殊,它是整个程序执行时的入口
编译器会主动把特定符号后的换行符转换为分号
格式化:
go fmt/vet/fix
goimports如果变量没有显式初始化,则被隐式地赋予其类型的零值(zero value)
The Origins of Go
Organization of the Book
four major kinds of declarations: var, const, type, and func
Not every value has an address, but every variable does
In Go, the sign of the remainder is always the same as the sign of the dividend, so -5%3 and -5%-3 are both -2
Usually when a function returns a non-nil error, its other results are undefined and should be ignored
When the error is ultimately handled by the program’s main function, it should provide a clear causal chain from the root problem to the overall failure, reminiscent of a NASA accident investigation:
genesis: crashed: no parachute: G-switch failed: bad relay orientation
anonymous functions that share a variable local; an anonymous function can access its enclosing function’s variables, including named results
the scope rules for loop variables
as a general rule, you should not attempt to recover from another package’s panic
two key principles of object-oriented programming, encapsulation and composition
method receiver have both type T and *T
We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. 过早优化是万恶之源 不成熟的优化是万恶之源
more predictable and less mysterious -- 语言的解谜游戏
In discussions of concurrency, when we say
x happens before y
, we don’t mean merely that x occurs earlier in time than y; we mean that it is guaranteed to do so and that all its prioreffects
, such as updates to variables, are complete and that you may rely on them. When x neither happens before y nor after y, we say thatx is concurrent with y
. This doesn’t mean that x and y are necessarily simultaneous, merely that we cannot assume anything about theirordering
. As we’ll see in the next chapter, it’s necessary to order certain events during the program’s execution to avoid the problems that arise when two goroutines access the same variable concurrently.