Go - Pointer
指针类型 指针类型是依托某一类型而存在的 对于某一类型 T,以 T 为基类型的指针类型为 *T unsafe.Pointer 不需要基类型,用于表示一个通用指针类型 任何指针类型和 unsafe.Pointer 可以相互显式转换 1234567type T struct{}func main() { var p *T var p1 = unsafe.Pointer(p) // 任意指针类型 -> unsafe.Pointer p = (*T)(p1) // unsafe.Pointer -> 任意指针类型} 如果指针类型变量没有被显式赋予初值,默认值为 nil 123456type T struct{}func main() { var p *T println(p == nil) // true} 给指针类型变量赋值 123var a int = 13var p *int = &a // &...
Go - Type Constraint
限制 对泛型函数的类型参数以及泛型函数中的实现代码设置限制 泛型函数的调用者只能传递满足限制条件的类型实参 泛型函数内部也只能以类型参数允许的方式使用这些类型实参值 在 Go 中,使用类型参数约束来表达这种限制条件 函数普通参数在函数实现中可以表现出来的性质与可以参与的运算由参数类型限制 泛型函数的类型参数由约束来限制 内置约束any 最宽松的约束 无论是泛型函数还是泛型类型,其所有的类型参数声明中都必须显式包含约束 可以使用空接口类型来表达所有类型 123func foo[T interface{}](sl []T) {}func bar[T1 interface{}, T2 interface{}](t1 T1, t2 T2) {} 空接口类型的不足:使得声明变得冗长、复杂、语义不清 builtin/builtin.go12// any is an alias for interface{} and is equivalent to interface&...
Go - Type Parameter
不支持 Feature Desc 泛型特化 编写一个泛型函数针对某个具体类型的特殊版本 元编程 编写在编译时执行的代码来生成在运行时执行的代码 操作符方法 不能将操作符视为方法并自定义其实现 变长类型参数 容器重复代码123456789101112131415161718192021222324252627func maxInt(sl []int) int { if len(sl) == 0 { panic("empty slice") } max := sl[0] for _, v := range sl[1:] { if v > max { max = v } } return max}func maxString(sl []string) string { if len(sl) == 0 { panic("empt...
Go - GC
逃逸分析 在传统的不带 GC 的编程语言中,需要关注对象的分配位置,是分配在堆上还是栈上 Go 集成了逃逸分析功能来自动判断对象是应该分配在堆上还是栈上 只有在代码优化时,才需要研究具体的逃逸分析规则 escape.go123456package mainfunc main() { var m = make([]int, 10240) println(m[0])} 1234$ go build -gcflags='-m' escape.go# command-line-arguments./escape.go:3:6: can inline main./escape.go:4:14: make([]int, 10240) escapes to heap 较大的对象会被放在堆上 如果对象分配在栈上,其管理成本比较低,只需要挪动栈顶寄存器就可以实现对象的分配和释放 如果对象分配在堆上,需要经过层层的内存申请过程 逃逸分析和垃圾回收结合,可以极大地降低开发者的心智负担,无需再担心内存的分配和释放 抽象成本 Rust - 零成本抽象 ...
Go - Module Maintainer
仓库布局单模块 首选:一个 repo 管理一个 module,一般情况下,module path 与仓库地址保持一致 go.mod123module github.com/zhongmingmao/srsmgo 1.18 如果对 repo 打 tag,该 tag 会成为 module 的版本号,对 repo 的版本管理即对 module 的版本管理 12345678$ tree.├── LICENSE├── go.mod├── pkg1│ └── pkg1.go└── pkg2 └── pkg2.go 该 module 对应的包导入路径为 github.com/zhongmingmao/srsm/pkg1 和 github.com/zhongmingmao/srsm/pkg2 如果 module 演进到了 2.x 版本,则包导入路径变更为 github.com/zhongmingmao/srsm/v2/pkg1 多模块1234567891011$ tree.├── LICENSE├── module1│ ├── go.mod│ └── pkg1│ └─...
Go - Generics
泛型概念 将算法与类型解耦,实现算法更广泛的复用 实现方向 拖慢程序员 不实现泛型,不会引入复杂性 但需要程序员花费精力重复实现同逻辑但不同类型的函数或者方法 拖慢编译器 – C++/Go 类似 C++ 的泛型实现方案,通过增加编译器的负担为每个类型实例生成一份单独的泛型函数的实现 产生大量的代码,且大部分是多余的 拖慢执行性能 – Java 类似 Java 的泛型实现方案,即伪泛型,通过隐式的装箱和拆箱操作消除类型的差异 虽然节省了空间,但代码执行效率低 泛型设计类型系统 静态强类型:C++/Java/Go;动态强类型:Python;动态弱类型:JavaScript Go 为强类型语言,而 JavaScript 为弱类型语言,Go 的类型强度高于 JavaScript Go 和 Python 都有较高的类型强度,但类型检查的时机不同 Go 是在编译期,而 Python 则在运行期 如果类型检查是发生在运行期,则为动态类型语言 动态类型不仅仅表现在变量的类型可以更改,在 OOP 的编程语言中,类的定义也可以动态修改 动态类型的优点 动态类型有更好...
Go - Private Module
导入本地 Module 借助 go.mod 的 replace 指示符 123require github.com/user/b v1.0.0replace github.com/user/b v1.0.0 => 本地源码路径 优化方案:Go workspace Private Module公网 对 private module 的拉取,不会走 GOPROXY 代理服务,也不会去 GOSUMDB 服务器做 Go 包的 hash 值校验 内网 更主流 方案 1 in-hourse goproxy 类似于 nexus 方案 2 推荐 Go 命令默认会对所有通过 goproxy 拉取到的 Go Module,进行 sum 校验(默认到 sum.golang.org) 为了跳过 sum 验证,需要将 private module 填到 GONOSUMDB 中 实践GOPROXY 编译 123456789$ mkdir goproxy$ cd goproxy/$ git clone https://github.com/goproxyio/goproxy$ cd gopro...
Go - 1.17
语法特性切片 -> 数组指针 数组切片化,转换后,数组将成为切片的底层数组 1234a := [3]int{11, 12, 13}sl := a[:]sl[1] += 10fmt.Printf("%T, %v\n", sl, sl) // []int, [11 22 13] 在 Go 1.17 之前,不支持将切片转换为数组类型,仅可以通过 unsafe 包以不安全的方式实现转换unsafe 包的安全性没有得到编译器和运行时的保证,尽量不要使用 1234sl := []int{11, 12, 13}var p = (*[3]int)(unsafe.Pointer(&sl[0]))p[1] += 10fmt.Printf("%v\n", sl) // [11 22 13] 从 Go 1.17 开始,支持从切片转换为数组类型指针 1234sl := []int{11, 12, 13}var p = (*[3]int)(sl)p[1] += 10fmt.Printf("%...
Go - Sync + Atomic
并发模型 Do not communicate by sharing memory; instead, share memory by communicating. Go 应用并发设计的主流风格:使用 channel 进行不同 goroutine 间的通信 sync:提供基于共享内存并发模型的低级同步原语,如互斥锁、读写锁、条件变量等 atomic:提供原子操作原语 Sync场景 需要高性能的临界区同步机制场景 - critical section channel 是一种高级同步原语,其自身的实现是构建在低级同步原语的基础上 因此,channel 自身的性能要略逊于低级同步原语,开销更大 12345678910111213141516171819202122232425262728293031323334353637383940414243var cs = 0 // critical sectionvar mu sync.Mutexvar c = make(chan struct{}, 1)func criticalSectionSyncByMutex() {...
Go - Channel
一等公民 Go 在语法层面将 channel 作为一等公民对待 可以像使用普通变量那样使用 channel 定义 channel 类型变量、给 channel 变量赋值 将 channel 作为参数传递给函数或者方法 将 channel 作为返回值从函数或者方法中返回 将 channel 发送到其它 channel 基本用法make 与 slice、struct、map 一样,channel 是复合数据类型,即声明 channel 类型变量时,必须给出具体的元素类型channel 类型变量在声明时,如果没有被赋予初值,默认值为 nil,即 nil channel(读写都阻塞) 12// 声明一个元素为 int 类型的 channel 类型变量var ch chan int slice、struct、map 都支持使用复合类型字面值作为变量初始值,channel 类型变量赋初值的唯一方法是 make 12ch1 := make(chan int) // 无缓冲ch2 := make(chan int, 5) // 有缓冲,缓冲大小为 5 send / receive c...















