Golang笔记

以后可能以golang谋生一段时间了,开个golang的笔记

安装golang (linux-amd64)

version=1.22.6
curl https://go.dev/dl/go${version}.linux-amd64.tar.gz -Lf -o /tmp/golang.tar.gz
rm -rf /usr/local/go && tar -C /usr/local -xzf /tmp/golang.tar.gz
if ! grep "go/bin" ~/.zshrc;then
  export PATH=$PATH:/usr/local/go/bin
  echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.zshrc
fi

或者到https://go.dev/dl/下载安装包安装

设置私有仓库等golang 环境变量

go env -w GOPROXY="https://goproxy.cn,https://proxy.golang.org,direct"
go env -w GOPRIVATE="*.xxxx.xxx,*.xxxxx.cn,git.xxxxxx.com"
go env -w GOSUMDB="sum.golang.google.cn"

设置go test的快捷命令

mkdir -p ~/bin
if ! grep "PATH=~/bin" ~/.zshrc;then
  export PATH=~/bin:$PATH
  echo 'export PATH=~/bin:$PATH' >> ~/.zshrc
fi
cat > ~/bin/testgo <<\EOF                    
go test -gcflags='all=-N -l' -v "$@"
EOF
chmod +x ~/bin/testgo

vscode设置

首先安装插件 golang.go。然后参考 codespaces devcontainers go feature install.sh,安装相关的依赖:

# Install Go tools that are isImportant && !replacedByGopls based on
# https://github.com/golang/vscode-go/blob/v0.38.0/src/goToolsInformation.ts
GO_TOOLS="\
    golang.org/x/tools/gopls@latest \
    honnef.co/go/tools/cmd/staticcheck@latest \
    golang.org/x/lint/golint@latest \
    github.com/mgechev/revive@latest \
    github.com/go-delve/delve/cmd/dlv@latest \
    github.com/fatih/gomodifytags@latest \
    github.com/haya14busa/goplay/cmd/goplay@latest \
    github.com/cweill/gotests/gotests@latest \ 
    github.com/josharian/impl@latest"
(echo "${GO_TOOLS}" | xargs -n 1 go install -v )2>&1 | tee  ./init_go.log

echo "Installing golangci-lint latest..."
curl -fsSL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | \
    sh -s -- -b "$HOME/go/bin" | tee  -a ./init_go.log

The extension depends on go, gopls, dlv and other optional tools. If any of the dependencies are missing, the ⚠️ Analysis Tools Missing warning is displayed. Click on the warning to download dependencies.See the tools documentation for a complete list of tools the extension depends on.

最后配置 launch.json:

注意:golang debug不能正确处理软链接,所以最好不要把项目放在软链接的文件夹中,或者配置 substitutePath,见Debug symlink directoriesgo.delveConfig settings(用于CodeLens里的debug test按钮)。其他高级配置可以见 vscode-go debugging

{
    // 使用 IntelliSense 了解相关属性。 
    // 悬停以查看现有属性的描述。
    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch Package",
            "type": "go",
            "request": "launch",
            "mode": "auto",
            "cwd": "${workspaceFolder}",
            "program": "${workspaceFolder}/cmd/${workspaceFolderBasename}",
            "output": "__debug_bin_main",
            "args": [
                "--addr=localhost:7788",
                "--refer=arloor"
                ,"--tls=true"
            ]
        },        
        {// 参考 https://blog.csdn.net/passenger12234/article/details/122930124
            "name": "Test Debug", // debug 单测
            "type": "go",
            "mode": "test",
            "request": "launch",
            "buildFlags": [
                "-a", // force rebuilding of packages that are already up-to-date. 
            ],
            // "program": "${relativeFileDirname}", // 当前打开的目录
            "program": "./internal/app",
            "output": "__debug_bin_test",
            "args": [
                "-test.v", // 使t.Log()输出到console
                // "-test.run",
                // "^TestGetLlamAccessPoint$"
                // "-test.bench",
                // "BenchmarkTranslateWithFallback",
                // "-test.benchmem"
            ],
        },
    ]
}

其中 Test debug 相当于:

go test -c github.com/arloor/xxxx/internal/app -o __debug_bin_test -gcflags='all=-N -l' # -gcflags是vscode自动加入的,用于关闭优化,使得可以断点调试
./__debug_bin_test -test.v -test.run="^TestGetRTMPAccessPoint$" -test.bench="BenchmarkTranslateWithFallback" -test.benchmem
# 可以参考 go help test, go help testflag

也需要注意 settings.json 中golang相关配置

{
    "go.lintTool": "golangci-lint",
    "go.toolsManagement.autoUpdate": true,
    "go.formatTool": "gofmt",
    "go.testExplorer.showOutput": true,
    "[go]": {
        "editor.formatOnSave": true,
        "editor.codeActionsOnSave": {
            "source.organizeImports": "always"
        }
    },
    "go.testFlags": [
        "-gcflags=all=-l", // 针对run test禁用内联优化,使gomonkey可以成功打桩。对debug test不生效,因为golang插件针对debug test自行设置了-gcflags="all=-l -N"
        "-v", // 使run test可以输出t.Logf的日志。对debug test不生效,只在test fail的时候才会打印t.Logf的日志
    ],
    "go.formatFlags": [
        "-w"
    ],
}

这样的配置下仍有一个棘手的问题难以解决:CodeLens的debug test难以打印 t.Logf 的日志,除非test fail,万般尝试都失败,最终放弃吧。

golangci-lint使用

参考文档:

需要在某些地方忽略某个 linter 时,可以注释 //nolint:xx,xx,这个注释可以用在go文件开头、行末、函数、结构体,分别代表不同的作用域。

配置见.golangci.yml

defer

  1. defer匿名函数中“捕获”的参数是执行时的最新值。而“传递”的参数是声明defer时确定的。
  2. defer函数可以修改返回值,只要返回值是“捕获”的
  3. 探讨defer和for、if一起使用的情况(这个有点坑的): Golang Defer: From Basic To Traps
package main

import "fmt"

func main() {
	i := 0
	defer func(n int) {
		fmt.Println("// catch", i)
		fmt.Println("// param", n)
	}(i)
	i = 1
	defer func(n int) {
		fmt.Println("// catch", i)
		fmt.Println("// param", n)
	}(i)
	i = 2
	defer func(n int) {
		fmt.Println("// catch", i)
		fmt.Println("// param", n)
	}(i)

	fmt.Println(deferModReturn())
	fmt.Println()
}

func deferModReturn() (str string) {
	str = "# raw"
	defer func() {
		str = "// defer modified"
	}()
	return str
}

// defer modified

// catch 2
// param 2
// catch 2
// param 1
// catch 2
// param 0

类型转换

go之4种类型转换-腾讯云开发者社区-腾讯云 (tencent.com)

  1. 显式转换: T(x)
  2. 隐式转换:不需要开发人员编写转换代码,由编译器自动完成。
  3. 类型断言:newT, ok := x.(T)
  4. unsafe.Pointer强制转换