go语言基础/易错点/部分源码 | 20250912

写在前面

最近面试过程中有被问到go语言相关的内容, 正好把知识库中的go语言基础(含易错点)整理处理了
由于是自己知识库中的笔记, 内容相当简略(目标用户是自己-_-), 各部分内容想参考详细内容的, 可以参考下面的资料

官网文档

社区中有不少优秀的资料

如果对阅读源码感兴趣的小伙伴, 推荐一个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=int32 len("汉") 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{}
  • 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
    • pkg/errors %v %+v errors.Wrap()
    • 上生产的代码务必不要忽略错误
    • 预期内err.使用value.ErrFoo=errors.New("foo").errors.Is 非预期err.使用type.type BarError struct{}.errors.As
    • %w.wrap %v.转换
  • panic/recover
    • 运行时产生/panic()
    • recover只在defer中有效; panic允许在defer中多次调用; panic只对当前goroutine的defer有效
    • panic 会一直出栈, 直到进程退出或 recover -> recover必须要写到defer中 -> recover必须包一层.检查recover返回值
    • 野生go.任何go中panic都会导致程序退出 -> go func(){}中必须捕获panic http/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
  • sync
    • Mutex RWMutex Cond: MustNotCopied
  • encoding
    • json: json:"user_id,omitempty,string" json:",default=pro,options=dev|test|rt|pre|pro,optional,range=[0:10]"
  • 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
http_server_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/list

  • test: 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 prior effects, 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 that x is concurrent with y. This doesn’t mean that x and y are necessarily simultaneous, merely that we cannot assume anything about their ordering. 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.

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容