for...range要点

range循环时,使用的是被迭代的元素的副本 type T struct { n int } func main() { ts := [2]T{} for i, t := range ts { switch i { case 0: t.n = 3 // 被访问的是ts的副本 ts[1].n = 9 case 1: fmt.Print(t.n, " ") } } fmt.Print(ts) } 输出:0 {{0} {9}} range 循环语句使用的临时变量 func main() { h := make([]*int, 3) u := []int{1, 2, 3} for i, v := range u { h[i] = &v } for i := range h { fmt....

March 12, 2022 · 1 min · 李昌

标准库之unsafe

1. Go中对指针的限制 Go 的指针不能进行数学运算。 不同类型的指针不能相互转换。 不同类型的指针不能使用 == 或 != 比较。只有在两个指针类型相同或者可以相互转换的情况下,才可以对两者进行比较。另外,指针可以通过 == 和 != 直接和 nil 作比较。 不同类型的指针变量不能相互赋值。 使用unsafe包,可以一定程度上打破这些限制,那么为什么要打破这些限制。请看下文。 2. unsafe.Pointer unsafe.Pointer的定义 type ArbitraryType int type Pointer *ArbitraryType unsafe 包提供了 2 点重要的能力: 任何类型的指针和 unsafe.Pointer 可以相互转换。 uintptr 类型和 unsafe.Pointer 可以相互转换。 pointer 不能直接进行数学运算,但可以把它转换成 uintptr,对 uintptr 类型进行数学运算,再转换成 pointer 类型。利用这两个对象的相互转换,就可以打破上述4个限制。 // uintptr 是一个整数类型,它足够大,可以存储 type uintptr uintptr 还有一点要注意的是,uintptr 并没有指针的语义,意思就是 uintptr 所指向的对象会被 gc 无情地回收.而 unsafe.Pointer 有指针语义,可以保护它所指向的对象在“有用”的时候不会被垃圾回收。 3. 利用unsafe获取slice和map的长度 slice和map的长度都存储在其内部变量中,因此我们先来看这两个结构体定义: // runtime/slice.go type slice struct { array unsafe....

March 4, 2022 · 2 min · 李昌

静态代码检查: golangci-lint

1. 简介 golangci-lint 是对golang进行静态代码检查的工具。其具有以下特性: 速度非常快:golangci-lint 是基于 gometalinter 开发的,但是平均速度要比 gometalinter 快 5 倍。golangci-lint 速度快的原因有三个:可以并行检查代码;可以复用 go build 缓存;会缓存分析结果。 可配置:支持 YAML 格式的配置文件,让检查更灵活,更可控。 IDE 集成:可以集成进多个主流的 IDE,例如 VS Code、GNU Emacs、Sublime Text、Goland 等。 linter 聚合器:1.41.1 版本的 golangci-lint 集成了 76 个 linter,不需要再单独安装这 76 个 linter。并且 golangci-lint 还支持自定义 linter。 最小的误报数:golangci-lint 调整了所集成 linter 的默认设置,大幅度减少了误报。 良好的输出:输出的结果带有颜色、代码行号和 linter 标识,易于查看和定位。 2. 安装 # 安装 go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.41.1 # 检查是否安装成功 golangci-lint version # 输出 golangci-lint 版本号,说明安装成功 golangci-lint has version v1.44.0 built from (unknown, mod sum: "h1:YJPouGNQEdK+x2KsCpWMIBy0q6MSuxHjkWMxJMNj/DU=") on (unknown) 3....

February 9, 2022 · 2 min · 李昌

channel的行为

1. nil channel 接收 接收goroutine阻塞 发送 发送个goroutine阻塞 2. 向无缓冲channel发送消息 接受队列有goroutine 接收端将收到消息 接收队列无goroutine 发送goroutine将阻塞 已有发送goroutine阻塞 发送goroutine将阻塞 3. 从无缓冲channel接收消息 无发送goroutine 接收端阻塞 有发送goroutine 收到消息 4. 向有缓冲channel发送消息 队列未满 正常发送 队列已满 发送端阻塞 5. 从有缓冲channel接收消息 队列中有消息 正常接收 队列中无消息 接收端阻塞 6. 对close channel的操作 向closed channel发送 panic...

January 2, 2022 · 1 min · 李昌

golang中的tag

1. tag的基本介绍 字段标签可以存储元信息,这些元信息可以使用反射来访问。通常这些元信息用来提供一个字段如何从一种格式编码至另一种格式的相关信息(或是数据应如何在数据库中存储等)。但实际上标签可以存储任何你想要的元信息,无论是你自己使用还是由另一个包使用。 就像reflect.StructTag文档中提到的那样,字段标签通常是由空格分割的key:"value"列表,例如: type User struct { Name string `json:"name" xml:"name"` } 其中的key通常表示后面"value"所对应的包,例如json这个key将被encoding/json这个包使用。 如果需要在"value"中传递多个值,那么通常使用,逗号来分割,例如: Name string `json:"name,omitempty" xml:"name"` 值为破折号通常代表在处理时忽略该字段,例如在json中代表不要序列化这个字段 2. 例子:获取自定义tag 我们可以使用反射包来获取结构体字段的值。首先我们需要获取结构体的Type,然后查询字段,可以使用Type.Field(i int)或者Type.FieldByName(name string)。这些方法返回一个代表结构体字段的StructField值和一个代表tag的类型为StructTag的StructField.Tag值。 前面我们提到,字段标签通常是由空格分割的key:"value"列表,如果你的确是这么做的,你可以使用StructTag.Get(key string)这个方法来获取这个key对应的value。如果你不是这么做的,Get()方法可能不能解析key:"value"对并找到你想要的标签。如果你没有遵循字段标签通常是由空格分割的key:"value"列表,那么你可能需要实现自己的解析逻辑。 go1.7中添加了StructTag.Lookup()方法,这个方法的行为类似于Get(),但其将不包含给定键的标签与将空字符串与给定键相关联的标签区分开来。 来看下面这个例子: type User struct { Name string `mytag:"MyName"` Email stirng `mytag:"MyEmail"` } u := User{"Bob", "bob@cc.com"} t := reflect.TypeOf(u) for _ fieldName := range []string{"Name", "Email"} { field, found := t.FieldByName(fieldName) if !found { continue } fmt.Printf("\nField: User.%s\n", fieldName) fmt.Printf("\tWhole tag value : %q\n", field....

September 14, 2021 · 2 min · 李昌

ORM之sqlc

1. 安装sqlc go get github.com/kyleconroy/sqlc/cmd/sqlc 2. 基本使用 建立基本项目结构 mkdir sqlc-demo cd sqlc-demo go mod init sqlc-demo 在sqlc-demo中建立如下目录结构: . ├── db │ ├── queries │ ├── schema │ └── sqlc └── go.mod 其中query中存储查询语句,schema中存储数据库表结构,sqlc中存储生成的代码。 基本表结构 sqlc-demo/db/schema/table.sql CREATE TABLE "accounts" ( "id" bigserial PRIMARY KEY, "owner" varchar NOT NULL, "balance" bigint NOT NULL, "currency" varchar NOT NULL, "created_at" timestamptz NOT NULL DEFAULT (now()) ); CREATE TABLE "entries" ( "id" bigserial PRIMARY KEY, "account_id" bigint NOT NULL, "amount" bigint NOT NULL, "created_at" timestamptz NOT NULL DEFAULT (now()) ); CREATE TABLE "transfers" ( "id" bigserial PRIMARY KEY, "from_account_id" bigint NOT NULL, "to_account_id" bigint NOT NULL, "amount" bigint NOT NULL, "created_at" timestamptz NOT NULL DEFAULT (now()) ); 配置文件 sqlc-demo/sqlc....

August 22, 2021 · 3 min · 李昌

数据库版本管理-migrate

migrate是一个golang写成的数据库版本迁移工具,可以用来方便的对数据库进行迁移和回退。 Github上有详细的教程等:https://github.com/golang-migrate/migrate 在安装时可能需要指定所用的驱动,否则会因缺少驱动无法连接数据库,例如go install -tags mysql github.com/golang-migrate/migrate/v4/cmd/migrate 建立目录 mkdir -p migrate-demo/db cd migrate-demo/db mkdir ddl mkdir -p schema/blog 现在migrate-demo目录下结构如下: . └── db ├── ddl └── schema └── blog 其中,ddl中存储建库的sql文件,schema存放建表的sql文件 建库 建库 vim db/ddl/blog.sql CREATE DATABASE IF NOT EXISTS blog DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; build镜像 编写Dockerfile vim db/Dockerfile FROMmysql:5.7COPY ./ddl /docker-entrypoint-initdb.d/ENV MYSQL_ROOT_PASSWORD=admin123 复制到/docker-entrypoint-initdb.d目录下的sql脚本会被自动执行 docker build -t mysql-demo -f ./Dockerfile . build成功后,使用docker images命令查看镜像:...

August 15, 2021 · 1 min · 李昌

依赖注入:wire包的使用

官方教程,写的很好,我就不多说了 Let’s learn to use Wire by example. The Wire guide provides thorough documentation of the tool’s usage. For readers eager to see Wire applied to a larger server, the guestbook sample in Go Cloud uses Wire to initialize its components. Here we are going to build a small greeter program to understand how to use Wire. The finished product may be found in the same directory as this README....

July 6, 2021 · 9 min · 李昌

5种goroutine池的实现之对比

1. wazsmwazsm/mortar(★74) 简单介绍 创建一个容量为 N 的池, 在池容量未满时, 每塞入一个任务(生产任务), 任务池开启一个 worker (建立协程) 去处理任务(消费任务)。 当任务池容量赛满,每塞入一个任务(生产任务), 任务会被已有的 N 个 worker 抢占执行(消费任务),达到协程限制的功能。但worker创建后不会回收,除非将整个pool撤销。 结构 type Task struct { Handler func(v ...interface{}) // 函数签名 Params []interface{} // 参数 } // Pool task pool type Pool struct { capacity uint64 // 池的容量,自行制定 runningWorkers uint64 // 正在运行的worker status int64 // 池的状态 chTask chan *Task // 任务队列,worker从中获取任务 PanicHandler func(interface{}) // 自定义的PanicHandler,防止因某个goroutine发生panic而导致服务崩溃。 sync.Mutex // 全局锁 } 核心代码...

June 19, 2021 · 13 min · 李昌

go generate工具

1. go generate go generate命令运行时,将找到源代码中所有包含//go:generate的特殊注释,提取并执行//go:generate后附加的命令。 基本语法: //go:generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages] 需要注意的几点: 该特殊注释必须在.go源码文件中。 每个源码文件可以包含多个generate特殊注释。 go generate只在运行go generate命令时运行,go build, go get, go test等其他命令不会运行它。 命令串行执行的,如果出错,就终止后面的执行。 特殊注释必须以"//go:generate"开头,双斜线后面没有空格。 简单的例子: package main import "fmt" //go:generate echo "world" func main() { fmt.Println("hello") } 运行结果: 在go generate命令中,还可以使用一些环境变量: $GOARCH The execution architecture (arm, amd64, etc.) $GOOS The execution operating system (linux, windows, etc.) $GOFILE The base name of the file....

May 27, 2021 · 3 min · 李昌