Go中的格式错误- %s %v或%w

qyyhg6bp  于 2022-12-20  发布在  Go
关注(0)|答案(2)|浏览(1031)

%s%v%w可以用来格式化Go语言中的错误(把它们转换成字符串)。
在Go语言自己的工具中,它们的用法似乎有所不同。
在cmd/go/内部/get/路径中。go:return fmt.Errorf("malformed import path %q: %v", path, err)
在cmd/go/内部/列表/列表.go中:base.Fatalf("%s", err)
我应该使用哪种格式字符串?

66bbxpm5

66bbxpm51#

我应该使用%s或%v来格式化错误吗?

**TL;DR;**两者都不是。在99.99%的情况下使用%w。在其余0.001%的情况下,%v%s可能“应该”具有相同的行为,但错误值为nil时除外。%vnil错误的友好输出可能是首选%v的原因(见下文)。

详情如下:

使用%w代替%v%s

从Go语言1.13开始(如果使用golang.org/x/xerrors的话,可以使用%w动词,但只能用于error值,它会将错误 Package 起来,以便以后可以用errors.Unwrap来展开,也可以用errors.Iserrors.As来考虑。
唯一不合适的情况是:
1.您必须支持旧版本的Go语言,并且xerrors不是一个选项。
1.您希望创建一个唯一的错误,而 * 不 * Package 一个现有的错误。这可能是合适的,例如,如果您在搜索用户时从数据库中得到一个Not found错误,并希望将其转换为Unauthorized响应。在这种情况下,您很少会使用带有 any 格式动词的原始错误值。

好的,那么%v%s又是怎么回事呢?

%s%v是如何实现的细节可以在文档中找到,我已经突出显示了与您的问题相关的部分。
1.如果操作数是一个reflect.Value,则操作数将被它所保存的具体值替换,并且打印将继续下一个规则。
1.**如果操作数实现了Formatter接口,则将调用该接口。**Formatter提供了对格式设置的精细控制。
1.如果%v动词与# flag(%#v)一起使用,并且操作数实现了GoStringer接口,则将调用该接口。

如果格式(对于Println等,隐式为%v)对字符串(%s %q %v %x %X)有效,则应用以下两个规则

1.如果操作数实现了error接口,则会调用Error方法将对象转换为字符串,然后根据 predicate (如果有)的要求格式化字符串。
1.如果操作数实现了String()string方法,则会调用该方法将对象转换为字符串,然后根据 predicate (如果有)的要求格式化字符串。
总之,fmt.*f函数将:
1.查找Format()方法,如果它存在,则调用它。
1.查找Error()方法,如果它存在,则调用它。
1.查找String()方法,如果它存在,就调用它。
1.使用一些默认格式。
因此,实际上,这意味着%s%v是相同的,除非错误类型上存在Format()方法(或者当错误是nil时)。当错误确实有Format()方法时,可能希望它会产生与%s%verr.Error()相同的输出。但由于这取决于错误的实现,因此没有任何保证,因此这里没有“正确答案”。
最后,如果您的错误类型支持%+v动词变体,那么如果您需要详细的输出,当然需要使用它。

nil个值

虽然很少(有意地)在nil错误时调用fmt.*f,但%s%v的行为确实不同:

%s: %!s(<nil>)
%v: <nil>

Playground link

628mspwn

628mspwn2#

使用%v作为误差值。

if err != nil {
    return fmt.Errorf("pack %v: %v", name, err)
}

但是,在Go 1.13中,fmt.Errorf函数支持一个新的%w predicate 。当此 predicate 存在时,fmt.Errorf返回的错误将使Unwrap方法返回%w的参数,这肯定是一个错误。在所有其他方面,%w%v相同。

if err != nil {
    // Return an error which unwraps to err.
    return fmt.Errorf("pack %v: %w", name, err)
}

需要区分%w%v的地方:

  • 读取代码块中的注解 *
f, err := os.Open(filename)
if err != nil {
    // The *os.PathError returned by os.Open is an internal detail.
    // To avoid exposing it to the caller, repackage it as a new
    // error with the same text.
    //
    //
    // We use the %v formatting verb, since
    // %w would permit the caller to unwrap the original *os.PathError.
    return fmt.Errorf("%v", err)
}

Read:对于错误,何时应该切换到w
此外,内置的error接口允许Go语言程序员添加任何他们想要的信息,它只需要一个实现Error方法的类型
示例:

type QueryError struct {
    Query string
    Err   error
}

func (e *QueryError) Error() string { return e.Query + ": " + e.Err.Error() }

因此,大多数示例都具有类似的实现类型,其中err具有Error方法,该方法返回string,您可以使用%s对其进行处理

相关问题