Help us shape the future of Go! Take the 2026 Go Developer Survey

must

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 20, 2026 License: MIT Imports: 0 Imported by: 0

README

must

must 用于简化线性流程中重复的 if err != nil。Get、OK 系列负责传播 error,Do、Call 系列在边界将其还原为 error。

适合错误策略只是“立即返回”的连续步骤。

环境要求

  • Go 1.25 或更高版本

安装

go get github.com/viocha/must
import "github.com/viocha/must"

为什么使用 must

假设一段逻辑需要依次解析端口、解析超时时间并校验配置,常规写法需要在每一步检查 error:

func loadSettings(rawPort, rawTimeout string) (int, time.Duration, error) {
	port, err := strconv.Atoi(rawPort)
	if err != nil {
		return 0, 0, err
	}

	timeout, err := time.ParseDuration(rawTimeout)
	if err != nil {
		return 0, 0, err
	}

	if err := validateSettings(port, timeout); err != nil {
		return 0, 0, err
	}

	return port, timeout, nil
}

使用 must 后,每个调用只保留业务返回值,错误在 Call2 边界统一返回:

func loadSettings(rawPort, rawTimeout string) (int, time.Duration, error) {
	return must.Call2(func() (int, time.Duration) {
		port := must.Get(strconv.Atoi(rawPort))
		timeout := must.Get(time.ParseDuration(rawTimeout))
		must.OK(validateSettings(port, timeout))
		return port, timeout
	})
}

闭包中连续调用了两次 Get 和一次 OK。任何一步失败都会立即终止后续逻辑,并由 Call2 返回对应的原始 error。

API

func OK(err error)
func Get[T any](v T, err error) T
func Get2[T1, T2 any](v1 T1, v2 T2, err error) (T1, T2)
func Get3[T1, T2, T3 any](v1 T1, v2 T2, v3 T3, err error) (T1, T2, T3)

func Do(fn func()) error
func Call[T any](fn func() T) (T, error)
func Call2[T1, T2 any](fn func() (T1, T2)) (T1, T2, error)
func Call3[T1, T2, T3 any](fn func() (T1, T2, T3)) (T1, T2, T3, error)

API 按业务返回值数量一一对应。Get3 / Call3 作为多返回值辅助的上限;返回值更多时建议定义 struct,而不是继续扩展 GetN / CallN:

业务返回值数量 将error 转为 panic 将 panic 转回error
0 OK Do
1 Get Call
2 Get2 Call2
3 Get3 Call3

使用 Do

Do 用于没有业务返回值、只需要依次完成多个操作的场景:

err := must.Do(func() {
	must.OK(os.MkdirAll(directory, 0o755))
	must.OK(os.WriteFile(filename, data, 0o644))
})
if err != nil {
	return err
}

如果创建目录失败,不会继续写入文件;如果写入文件失败,Do 会返回 os.WriteFile 产生的原始 error。

使用 Call

Call 用于返回一个业务值:

config, err := must.Call(func() Config {
	data := must.Get(os.ReadFile(filename))
	var config Config
	must.OK(json.Unmarshal(data, &config))
	must.OK(config.Validate())
	return config
})

读取、解析或校验任意一步失败时,config 为 Config 的零值,err 为失败步骤产生的原始 error。

使用 Call2 和 Call3

Call2 用于返回两个业务值:

user, token, err := must.Call2(func() (User, string) {
	user := must.Get(loadUser(userID))
	token := must.Get(createToken(user))
	must.OK(saveLoginRecord(user, token))
	return user, token
})

Call3 用于返回三个业务值:

host, port, secure, err := must.Call3(func() (string, int, bool) {
	data := must.Get(os.ReadFile(filename))
	config := must.Get(parseServerConfig(data))
	must.OK(config.Validate())
	return config.Host, config.Port, config.Secure
})

发生错误时,Call2 或 Call3 会将所有业务返回值置为对应类型的零值,并返回原始 error。

错误上下文

must 只负责传播 error,不会自动补充错误上下文。建议仍在错误跨越语义层级的位置使用 %w 包装,例如:

func loadUser(id int64) (User, error) {
	user, err := queryUser(id)
	if err != nil {
		return User{}, fmt.Errorf("query user %d: %w", id, err)
	}
	return user, nil
}

之后可以继续使用 must.Get(loadUser(id)) 做传播。Do 和 Call 系列返回原始 error,因此不会额外破坏已有的 errors.Is / errors.As 错误链。

panic 处理范围

Do、Call、Call2 和 Call3 只捕获由本包 OK、Get、Get2 或 Get3 触发的 panic。业务代码主动触发的 panic 和 Go runtime panic 不会被吞掉,而是保持原值继续向上传播。

err := must.Do(func() {
	value := must.Get(loadValue())
	must.OK(saveValue(value))
	panic("unexpected")
})

上面的 unexpected 不会被转换为 error,而会继续向上传播。

panic 只能在同一个 goroutine 中被 recover。不要期望外层的 Do 或 Call 捕获子 goroutine 中触发的 panic。

runtime.Goexit 也不会被转换成 error;它会继续终止当前 goroutine。

本库不支持 GODEBUG=panicnil=1 遗留兼容模式。该模式会使 recover 无法区分 panic(nil) 和 runtime.Goexit;请使用 Go 1.25 的默认运行时行为。

不要在可能于 panic 展开阶段执行的 defer 中使用 OK 或 Get 系列函数。例如:

defer func() {
	must.OK(file.Close())
}()

如果函数主体已经发生 panic,上面的 defer 再触发一个 must panic,会覆盖原 panic,外层 Do / Call 最终只能看到后一个错误。这是 Go 的 panic/defer 语义导致的。清理逻辑中的错误应使用显式处理。

如果 OK 或 Get 系列触发的 panic 没有被 Do 或 Call 系列捕获,输出会同时包含 must 标记和原始错误消息:

panic: must: 原始错误消息

Documentation

Overview

Package must 提供一组简化线性流程错误传播的辅助函数。 Get、OK 系列通过带标记的 panic 传播 error,Do、Call 系列在边界将其还原。

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Call

func Call[T any](fn func() T) (v T, err error)

Call 执行 fn;出错时返回 T 的零值和 error。

func Call2

func Call2[T1, T2 any](fn func() (T1, T2)) (v1 T1, v2 T2, err error)

Call2 执行 fn;出错时返回 T1、T2 的零值和 error。

func Call3

func Call3[T1, T2, T3 any](fn func() (T1, T2, T3)) (v1 T1, v2 T2, v3 T3, err error)

Call3 执行 fn;出错时返回 T1、T2、T3 的零值和 error。

func Do

func Do(fn func()) (err error)

Do 执行 fn,并返回传播出的 error。其他 panic 会继续向上传播。

func Get

func Get[T any](v T, err error) T

Get 返回 v;err 非 nil 时传播该错误。

func Get2

func Get2[T1, T2 any](v1 T1, v2 T2, err error) (T1, T2)

Get2 返回 v1 和 v2;err 非 nil 时传播该错误。

func Get3

func Get3[T1, T2, T3 any](v1 T1, v2 T2, v3 T3, err error) (T1, T2, T3)

Get3 返回 v1、v2 和 v3;err 非 nil 时传播该错误。

func OK

func OK(err error)

OK 在 err 非 nil 时传播该错误。

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL