Skip to main content

Go FAQ

caution
  • golang/go#9200
    • html/template JS 不能包含 ```
  • golang/go#13492
    • musl 不支持 c-shared
    • 因为 c-shared 用到了 glibc 扩展
    • initial-exec TLS resolves to dynamic definition
tidy:
go mod tidy
fmt: tidy
go fmt ./...
outdated:
go list -u -m -f '{{if .Update}}{{.}}{{end}}' all
update:
go get -u ./...

安装

# Windows go1.17.6.windows-amd64.zip
# macOS go1.18.3.darwin-amd64.tar.gz
curl -LO https://mirrors.ustc.edu.cn/golang/go1.17.6.windows-amd64.zip
mkdir -p ~/sdk
unzip go1.17.6.windows-amd64.zip -d ~/sdk
mv ~/sdk/go ~/sdk/go1.17.6
~/sdk/go1.17.6/bin/go version

export PATH=$HOME/sdk/go1.17.6/bin:$PATH

安装指定版本 Golang 环境

  • 下载位置
    • HOME/sdk/goHOME/sdk/goVERSION/go$VERSION.darwin-amd64.tar.gz
  • 安装逻辑 [golang.org/dl/internal/version/version.go](https://cs.opensource.google/go/dl/+/1eec6072:internal/version/version.go
  • 安装完成不会删除压缩包,可自己删除
  • tip 最新 latest
    • 下载源码进行编译
  • 默认下载地址 "https://dl.google.com/go/" + version + "." + goos + "-" + arch + ext
# 下载 tip 版本
go install golang.org/dl/gotip@latest
gotip download

# 下载最新版本
# 从 dl.google.com 下载
# 默认安装到 ~/sdk/
# 其他版本 例如 go1.18rc1
go install golang.org/dl/go1.18@latest
go1.18 download
go1.18 env GOROOT
~/sdk/go1.18/bin/go env

# 安装到别的地方
HOME=/opt ~/go/bin/go1.18 download
/opt/sdk/go1.18/bin/go env

# 交叉编译
mkdir work && cd work
go mod init work
go get -u golang.org/dl/go1.18
GOOS=linux go build -o go1.18_linux golang.org/dl/go1.18@latest

# AlpineLinux 基础依赖
apk add libc6-compat gcompat
# AlpineLinux CGO 依赖
apk add gcc musl-dev

# 注意设置 GOROOT - 默认 /usr/go
export GOROOT=/opt/sdk/go1.18

export PATH="$GOROOT/bin:$HOME/go/bin:$PATH"

tip 版本

  • @latest - 稳定版本
  • @main, @master, @HEAD -> @tip
    • 最新的提交
    • 借用自 Mercurial

iota

GOVCS disallows using git for public

添加 GOVCS 设置,默认为 public:git|hg,private:all

# 全部允许
GOVCS=*:all go get github.com/wenerme/apki
# 限定
GOVCS=github.com:git,*:off go get github.com/wenerme/apki

go: cannot find GOROOT directory: /usr/local/go

  • 从源码构建默认 GOROOT_FINAL=/usr/local/go
  • AlpineLinux 默认为 /usr/lib/go
  • bazel 安装的 Go 也有这个问题

  1. 创建目录 - 推荐方式
ln -s /opt/sdk/go1.18beta1 /usr/local/go
  1. 修改 GOROOT 配置
GOROOT=/opt/sdk/go1.18beta1 /opt/sdk/go1.18beta1/bin/go env
# 写入后便不会报错
GOROOT=/opt/sdk/go1.18beta1 /opt/sdk/go1.18beta1/bin/go env -w GOROOT=/opt/sdk/go1.18beta1
/opt/sdk/go1.18beta1/bin/go env

golang.org/x

查找用到了 cgo 的模块

go list -f "{{if .CgoFiles}}{{.ImportPath}}{{end}}" $(go list -f "{{.ImportPath}}{{range .Deps}} {{.}}{{end}}" ./...)

JSON string to int

  • json.Number - encode 保留 number
  • json:",string" - encode 会转 string

sql null

  • 直接使用指针
    • 简单、无外部依赖
    • json、mapstructure 能正确处理
    • 代码逻辑复杂一点
    • 可能导致未预期的修改
  • sql.NullType
    • 代码逻辑清晰,不会导致指针修改
    • json、mapstructure 无法正确处理
    • mapstructure 需要自定义 Hook
  • null.Type - guregu/null
    • 引入外部库
    • 提供便捷方法
    • 支持 json
    • mapstructure 无法正确处理
    • mapstructure 需要自定义 Hook

Struct 是否使用指针

type Server struct {
// 内部配置对象可使用 Struct
conf ServerConf
}
// 因为会对 conf 进行默认值补齐 - 因此传入指针
func NewServer(conf *ServerConf)*Server{
// 修改
if conf.Bind == "" {
conf.Bind = "0.0.0.0"
}
// 复制一个 conf 避免外部更改
// Server 使用指针,因为不需要副本
return &Server{ Conf = *conf }
}

text/template vs html/template

  • html/template
    • 输出内容被转义,避免代码注入

不会使用 /etc/hosts 就行解析

  • 添加 /etc/nsswitch.conf 可以解决
    • echo "hosts: files dns" > /etc/nsswitch.conf
  • #35305 - net: prefer /etc/hosts over DNS when no /etc/nsswitch.conf is present
  • #22846 - net: Go DNS resolver does not read /etc/hosts

/etc/nsswitch.conf

# /etc/nsswitch.conf
#
# As described on the web page https://man7.org/linux/man-pages/man3/gethostbyname.3.html,
# without the nsswitch.conf file, the gethostbyname() and gethostbyaddr() domain queries
# will fail to a local name server, thus the /etc/hosts will take no effect.
#
# For example, when hostaliases are specified for a kubernetes pod, without proper settings
# defined in this file, the hostaliases settings will not take effect.
#
# Following contents of this file is from the ubuntu:16.04 docker image.

passwd: compat
group: compat
shadow: compat
gshadow: files

hosts: files dns
networks: files

protocols: db files
services: db files
ethers: db files
rpc: db files

netgroup: nis

Windows 安装

# msys 下
export GOPATH=$HOME/go
export PATH="$PATH:/c/GO/bin:$HOME/go/bin"
export GO111MODULE=on
export GOPROXY=https://goproxy.io

reflect.Value.Interface: cannot return value obtained from unexported field or method

relfect 不允许访问未导出字段

bufio.Reader vs bufio.Scanner

  • bufio.Scanner
    • 一次读一行 - 不包含分隔符 \r\n
    • 默认 64k 行限制
    • 接口使用友好,Scan 和 错误 独立
    • io.EOF 时 Err 为 nil
  • bufio.Reader
  • 内部 4k 缓冲
  • ReadLine 不返回换行,类似 Scanner,但返回 []byte - 不推荐 使用
  • ReadString - 读取直到指定分隔符,返回分隔符 - 类似 Scanner
  • 实现 io.Reader - 很多时候这个是选择的 决定因素

pq vs pgx

pq 作者推荐推荐使用 pgx

  • pq
    • 项目处于维护模式
  • pgx
    • 支持所有 native 类型
    • 支持逻辑复制协议

gc vs gccgo

  • gc - 默认 Golang 实现
    • 半年升级一次 - 跟随 spec 版本
    • 1.5 后不再依赖 C 编译器
    • 跨平台编译 - 不依赖 CGO 时
    • 静态编译 - 体积大 - HelloWorld 2M+
  • gccgo - 基于 GCC 实现
    • 随 GCC 升级 - 版本一般落后,升级慢
    • 依赖 OS 提供 GCC 包 - 一般落后主 GCC 版本
    • 编译更快,但支持更多优化 - 重 CPU 场景性能更好
    • 默认支持 CGO
    • 支持更多平台 - 所有 GCC 支持的平台
    • 交叉编译非常难
    • 动态链接 - 体积非常小 - HelloWorld 250K vs 2MB
      • libgo, libm, libgcc, libz, libpthread, ld.so, linux-vdso.so - virtual shared object
# go 可直接使用 gccgo 编译
go build -compiler gccgo myprog

# gccgo flags
go build -gccgoflags "-s -w" main.go

compile: version does not match go tool version

注意调整 GOROOT

Cipher CBC / ECB / CFB / GCM

memstats.gc_sys undefined (type mstats has no field or method gc_sys)

升级后出现,尝试卸载重装。

尝试删除目录 /usr/lib/go/ 后重装。

go.sum h1

<module> <version> <hash>
<module> <version>/go.mod <hash>

GODEBUG

  • GODEBUG=netdns=go
  • GODEBUG=netdns=cgo
  • GODEBUG=netdns=go+2
  • GODEBUG=netdns=cgo+2

常见大写字段名字

ACL
API
ASCII
CPU
CSS
DNS
EOF
GUID
HTML
HTTP
HTTPS
ID
IP
JSON
LHS
QPS
RAM
RHS
RPC
SLA
SMTP
SQL
SSH
TCP
TLS
TTL
UDP
UI
UID
URI
URL
UTF8
UUID
VM
XML
XMPP
XSRF
XSS

regexp

go build static

CGO_ENABLED=0 go build -a -ldflags '-extldflags "-static"'
# statically linked PIE
CGO_ENABLED=1 go build -buildmode=pie -tags 'osusergo,netgo,static,static_build' -ldflags '-linkmode=external -extldflags "-static-pie"' .

GOOS=linux go build -tags 'osusergo netgo'
GOFLAGS=-static

CGO_ENABLED=0 go build -a -tags 'osusergo netgo' -ldflags '-extldflags "-static"'

struct 可比较

  • 如果所有字段可比较,则 struct 可比较
  • 使用 struct 作为 context key 需要注意比较逻辑
    • 指针和非指针比较逻辑相同
// nocmp is an uncomparable struct. Embed this inside another struct to make
// it uncomparable.
//
// type Foo struct {
// nocmp
// // ...
// }
//
// This DOES NOT:
//
// - Disallow shallow copies of structs
// - Disallow comparison of pointers to uncomparable structs
type nocmp [0]func()

判断 int 类型

package main

import (
"fmt"
"runtime"
"unsafe"
)

func main() {
fmt.Println("arch", runtime.GOARCH)
// 8 -> int64
// 4 -> int32
fmt.Println("int", unsafe.Sizeof(int(0)))
}

内存模型

This program can only be run on AMD64 processors with v3 microarchitecture support

unknown-unsupported file format error

  • 检查下是不是有 .syso
    • macOS 下用不了

.syso

Go 语言项目中使用的一种资源文件,主要用于将静态资源(如图标、版本信息、Windows 清单文件等)嵌入到编译后的二进制文件中。

  • windres
    • .rc -> .syso

SysProcAttr

type Credential struct {
Uid uint32 // User ID.
Gid uint32 // Group ID.
Groups []uint32 // Supplementary group IDs.
NoSetGroups bool // If true, don't set supplementary groups
}

type SysProcAttr struct {
// linux like

Chroot string // Chroot.
Credential *Credential // Credential.
Ptrace bool // Enable tracing.
Setsid bool // Create session.
// Setpgid sets the process group ID of the child to Pgid,
// or, if Pgid == 0, to the new child's process ID.
Setpgid bool
// Setctty sets the controlling terminal of the child to
// file descriptor Ctty. Ctty must be a descriptor number
// in the child process: an index into ProcAttr.Files.
// This is only meaningful if Setsid is true.
Setctty bool
Noctty bool // Detach fd 0 from controlling terminal
Ctty int // Controlling TTY fd
// Foreground places the child process group in the foreground.
// This implies Setpgid. The Ctty field must be set to
// the descriptor of the controlling TTY.
// Unlike Setctty, in this case Ctty must be a descriptor
// number in the parent process.
Foreground bool
Pgid int // Child's process group ID if Setpgid.

//region Linux

// Pdeathsig, if non-zero, is a signal that the kernel will send to
// the child process when the creating thread dies. Note that the signal
// is sent on thread termination, which may happen before process termination.
// There are more details at https://go.dev/issue/27505.
Pdeathsig Signal
Cloneflags uintptr // Flags for clone calls.
Unshareflags uintptr // Flags for unshare calls.
UidMappings []SysProcIDMap // User ID mappings for user namespaces.
GidMappings []SysProcIDMap // Group ID mappings for user namespaces.
// GidMappingsEnableSetgroups enabling setgroups syscall.
// If false, then setgroups syscall will be disabled for the child process.
// This parameter is no-op if GidMappings == nil. Otherwise for unprivileged
// users this should be set to false for mappings work.
GidMappingsEnableSetgroups bool
AmbientCaps []uintptr // Ambient capabilities.
UseCgroupFD bool // Whether to make use of the CgroupFD field.
CgroupFD int // File descriptor of a cgroup to put the new process into.
// PidFD, if not nil, is used to store the pidfd of a child, if the
// functionality is supported by the kernel, or -1. Note *PidFD is
// changed only if the process starts successfully.
PidFD *int

//endregion

//region Windows
// https://learn.microsoft.com/en-us/windows/win32/procthread/process-creation-flags

HideWindow bool // 隐藏 Promopt/提示窗口 0x08000000 CREATE_NO_WINDOW
CmdLine string // 若非空,则使用此命令行;否则,根据传递给 StartProcess 的参数构建
CreationFlags uint32
Token Token // 在该 token 表示的安全上下文中运行新进程
ProcessAttributes *SecurityAttributes // 应用这些安全属性作为新进程的描述符
ThreadAttributes *SecurityAttributes // 应用这些安全属性作为新进程主线程的描述符
NoInheritHandles bool // 新进程不继承任何句柄,甚至包括 ProcAttr.Files 中的标准句柄,以及 AdditionalInheritedHandles 中的句柄
AdditionalInheritedHandles []Handle // 已标记为可继承的新进程将继承的额外句柄列表
ParentProcess Handle // 新进程将该句柄指定的进程视为父进程,且 AdditionalInheritedHandles(如果设置)应该存在于该父进程中

//endregion
}

文件名

fnfornode
snake_case.go文件名不强制
internal/内部包
Go Testing
x_test.go测试文件
testdata/
Go Build Constraints
x_GOOS.go//go:build OS
x_linux.go
x_windows.go
x_GOARCH.go
x_amd64.go
x_GOOS_GOARCH