哨兵错误是一种常见的错误处理模式,涉及与全局变量的比较。标准库中的 oserror
包提供了一个很好的例子,展示了如何通常定义这些:
package oserror
import "errors"
var (
ErrInvalid = errors.New("invalid argument")
ErrPermission = errors.New("permission denied")
ErrExist = errors.New("file already exists")
ErrNotExist = errors.New("file does not exist")
ErrClosed = errors.New("file already closed")
)
它们具有全局变量的所有常见警告,即它们可以被重新分配:
package main
import (
"fmt"
"os"
)
func main() {
var nilFile *os.File
// prints 0, error("invalid argument")
fmt.Println(nilFile.Read(nil))
// allowed :(
os.ErrInvalid = nil
// segfaults due to an internal error check being bypassed
fmt.Println(nilFile.Read(nil))
}
使用类型化常量定义这些的替代方法(向后兼容?)是可能的:
package oserror
// no imports needed! :)
// unexported error type means that no other package
// can define an error equal to any of the below constants,
// even if the string values match.
type osError string
func (err osError) Error() string {
return string(err)
}
// constants can't be reassigned :)
const (
// errors are unique as long as the strings are unique.
ErrInvalid = osError("invalid argument")
ErrPermission = osError("permission denied")
ErrExist = osError("file already exists")
ErrNotExist = osError("file does not exist")
ErrClosed = osError("file already closed")
)
我还没有考虑这种方法的潜在缺点或长期影响。
6条答案
按热度按时间50few1ms1#
谢谢,这是一个好主意。但是我们现在不能做出这样的改变,因为它会破坏Go 1的兼容性保证。
kmbjn2e32#
我认为一个更好的替代方案,可以省略分配,是使用零大小的专用类型:
从技术上讲,可以将它们重新分配,
os.ErrInvalid = os.ErrInvalid
是有效的代码,但是它什么都不做,所以没问题,每次有人return
或errors.Is
错误值时都避免分配。Nvm将常量字符串作为接口传递时,会将字符串头分配为全局变量,但我仍然认为像var ErrInvalid errors.Error[osError, "invalid argument"]
这样的东西更好。能找到一种不那么冗长的表达方式就好了。
如果我们可以将
const
作为类型参数传递,就可以这样做:这样的辅助函数可以轻松地创建 Package 父错误以进行通配符检查的单独错误:
7vux5j2d3#
使用常量字符串错误的一个缺点是,我们可以很容易地发明出相同类型的新错误:
也许,添加一个验证规则是个好主意:查找所有对全局错误值的修改(包括获取全局错误值地址的操作)。
drnojrws4#
如果我们能将
const
作为类型参数,就可以这样写:此外,已经有关于
const
类型参数的先例:数组f2uvfpb95#
Without getting too off track, has Go considered adding a
val
qualifier to ensure the variable can't be re-assigned post initialization ? I'd be interested in reading a thread about it if there is one. I've read through several proposals about adding immutable types and found myself agreeing with the arguments against it, but it seems like preventing re-assignment and leaving interior mutability to the type (i.e no setters) might be middle ground solution that could apply to more than just errors.ifmq2ha26#
@psnilesh 这里有 https://go.dev/issue/21130 。