You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// goods, err:=store.New()
iferr!=nil {
returnfmt.Errorf(
"new store: %w", err)
}
// bads, err:=store.New()
iferr!=nil {
returnfmt.Errorf(
"failed to create new store: %w", err)
}
错误命名
对于存储为全局变量的错误值,根据是否导出,使用前缀 Err 或 err
var (
// 导出以下两个错误,以便此包的用户能将它们与 errors.Is 进行匹配。ErrBrokenLink=errors.New("link is broken")
ErrCouldNotOpen=errors.New("could not open")
// 这个错误没有被导出,因为不想让它成为公共 API 的一部分。可能仍然在带有错误的包内使用它。errNotFound=errors.New("not found")
)
// $GOROOT/src/runtime/alg.gofuncefaceeq(t*_type, x, y unsafe.Pointer) bool {
ift==nil {
returntrue
}
eq:=t.equalifeq==nil {
panic(errorString("comparing uncomparable type "+t.string()))
}
ifisDirectIface(t) {
// Direct interface types are ptr, chan, map, func, and single-element structs/arrays thereof.// Maps and funcs are not comparable, so they can't reach here.// Ptrs, chans, and single-element items can be compared directly using ==.returnx==y
}
returneq(x, y)
}
funcifaceeq(tab*itab, x, y unsafe.Pointer) bool {
iftab==nil {
returntrue
}
t:=tab._typeeq:=t.equalifeq==nil {
panic(errorString("comparing uncomparable type "+t.string()))
}
ifisDirectIface(t) {
// See comment in efaceeq.returnx==y
}
returneq(x, y)
}
funcWaitExample(itemIds []int) int {
wgsync.WaitGroupresults:=make([]int, len(itemIds))
fori:=0; i<len(itemIds); i++ {
go (idxint) {
wg.Add(1) // data raceresults[idx] = ..
wg.Done()
}(i)
}
wg.Wait() // data race
}
funcDo() {
varwg sync.WaitGroupgofunc() {
deferclean(&locationErr) // locationErr data racedeferwg.Done()
// ...
}
wg.Wait()
iflocationErr!=nil { // data race
}
}
unsafe.Pointer
使用 go vet 来保证代码的严谨性
package main
import (
"fmt""unsafe"
)
funcmain() {
// An array of contiguous uint32 values stored in memory.arr:= []uint32{1, 2, 3}
// The number of bytes each uint32 occupies: 4.constsize=unsafe.Sizeof(uint32(0))
// Take the initial memory address of the array and begin iteration.p:=uintptr(unsafe.Pointer(&arr[0]))
fori:=0; i<len(arr); i++ {
// Print the integer that resides at the current address and then// increment the pointer to the next value in the array.fmt.Printf("%d ", (*(*uint32)(unsafe.Pointer(p))))
p+=size
}
}
表面是会输出 1 2 3,但是使用 go vet
go vet .
# github.com/mdlayher/example
./main.go:20:33: possible misuse of unsafe.Pointer
规则说明:
Converting a Pointer to a uintptr produces the memory address of the value pointed at, as an integer. The usual use for such a uintptr is to print it.
Conversion of a uintptr back to Pointer is not valid in general.
A uintptr is an integer, not a reference. Converting a Pointer to a uintptr creates an integer value with no pointer semantics. Even if a uintptr holds the address of some object, the garbage collector will not update that uintptr’s value if the object moves, nor will that uintptr keep the object from being reclaimed
分裂出来的代码是
p:=uintptr(unsafe.Pointer(&arr[0]))
// What happens if there's a garbage collection here?fmt.Printf("%d ", (*(*uint32)(unsafe.Pointer(p))))
因为把地址值存到了 p 中,没有立即使用,那么可能其中发生了垃圾回收。使用将 unsafe.Point 类型指针转换成 uintpr 后,是不能狗直接转回到 unsafe.Pointer 的,但是只有一种情况例外
If p points into an allocated object, it can be advanced through the object by conversion to uintptr, addition of an offset, and conversion back to Pointer.
也就是执行指针的算术运算逻辑
package main
import (
"fmt""unsafe"
)
funcmain() {
// An array of contiguous uint32 values stored in memory.arr:= []uint32{1, 2, 3}
// The number of bytes each uint32 occupies: 4.constsize=unsafe.Sizeof(uint32(0))
fori:=0; i<len(arr); i++ {
// Print an integer to the screen by:// - taking the address of the first element of the array// - applying an offset of (i * 4) bytes to advance into the array// - converting the uintptr back to *uint32 and dereferencing it to// print the valuefmt.Printf("%d ", *(*uint32)(unsafe.Pointer(
uintptr(unsafe.Pointer(&arr[0])) + (uintptr(i) *size),
)))
}
}
参考资料
init 使用
错误类型
几种使用场景
错误包装
错误命名
一次处理错误
优先使用 strconv 而不是 fmt
strconv
速度比fmt
快。避免字符串到字节的转换
不要一劳永逸使用 goroutine
等待 goroutine 退出
包名
局部变量的声明
如果将变量明确设置为某个值,则应使用短变量声明形式 (:=)。
nil 是一个有效的 slice
Channel 的 size 要么是 1,要么是无缓冲的
在边界处拷贝 Slices 和 Maps
使用 defer 来释放资源
避免字符串到字节的转换
尽量初始化时指定 Map 容量
避免使用全局变量
不要 panic
避免在公共结构中嵌入类型
这些嵌入的类型泄漏实现细节、禁止类型演化和模糊的文档。
假设共享的AbstractList实现了多种列表类型,请避免在具体的列表实现中嵌入 AbstractList。
相反,只需手动将方法写入具体的列表,该列表将委托给抽象列表。
Go 允许类型嵌入作为继承和组合之间的折衷。
外部类型获取嵌入类型的方法的隐式副本。
默认情况下,这些方法委托给嵌入实例的同一方法。
结构还获得与类型同名的字段。
很少需要嵌入类型。
无论是使用嵌入式结构还是使用嵌入式接口,嵌入式类型都会限制类型的演化.
尽管编写这些委托方法是乏味的,但是额外的工作隐藏了实现细节,留下了更多的更改机会,
还消除了在文档中发现完整列表接口的间接性操作。
判断接口类型的变量是否相等
这回对于接口类型变量的相等性判断一目了然了 (由 efaceeq 中 isDirectIface 函数上面的注释可见)!
常见的数据竞争
循环索引变量捕获导致的 data race
由于错误变量捕获导致的 data race
由于命名的返回变量被捕获,导致数据 data race。
slice 是令人困惑的类型,会产生难以察觉的的 data race
对 map 并发读写导致了频繁的 data race
混合使用 channel 和共享内存使代码变得复杂,容易发生 data race
sync.WaitGroup 不正确的 Add 或者 Done 产生 data race
unsafe.Pointer
使用 go vet 来保证代码的严谨性
表面是会输出 1 2 3,但是使用 go vet
规则说明:
分裂出来的代码是
因为把地址值存到了 p 中,没有立即使用,那么可能其中发生了垃圾回收。使用将 unsafe.Point 类型指针转换成 uintpr 后,是不能狗直接转回到 unsafe.Pointer 的,但是只有一种情况例外
也就是执行指针的算术运算逻辑
这样使用后,就正常了
golang 中的 socket 编程
socket 无数据
如果对方未发送数据到 socket,接收方 (Server) 会阻塞在 Read 操作上,这和前面提到的“模型”原理是一致的。执行该 Read 操作的 goroutine 也会被挂起。runtime 会监视该 socket,直到其有数据才会重新调度该 socket 对应的 Goroutine 完成 read
有部分数据
如果 socket 中有部分数据,且长度小于一次 Read 操作所期望读出的数据长度,那么 Read 将会成功读出这部分数据并返回,而不是等待所有期望数据全部读取后再返回
Socket 中有足够数据
如果 socket 中有数据,且长度大于等于一次 Read 操作所期望读出的数据长度,那么 Read 将会成功读出这部分数据并返回。这个情景是最符合对 Read 的期待的了:Read 将用 Socket 中的数据将传入的 slice 填满后返回:n = 10, err = nil。
socket 关闭
如果 client 端主动关闭了 socket,那么 Server 的 Read 将会读到什么呢?这里分为“有数据关闭”和“无数据关闭”。
#type/golang #public
The text was updated successfully, but these errors were encountered: