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

问题描述 投票:0回答:2

%s%v都可以用来格式化Go中的错误,并且至少在表面上似乎没有功能上的区别。

我们在Go自己的工具中都看到了。

cmd/go/internal/get/path.go中:return fmt.Errorf("malformed import path %q: %v", path, err)

cmd/go/internal/list/list.go中:base.Fatalf("%s", err)

我应该比另一个更喜欢吗?

string go error-handling formatting
2个回答
0
投票

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

TL; DR;都没有。在99.99%的情况下使用%w。在其他0.001%的情况下,%v%s可能“应该”表现相同,但是没有保证。

现在为详细信息:

使用%w代替%v%s

从Go 1.13开始(或更早,如果使用golang.org/x/xerrors,则可以将%w动词用于仅error值),该动词将错误包装起来,以便以后可以用errors.Unwrap进行包装。以便可以与errors.Iserrors.As一起考虑。

唯一不适合的时间:

  1. 您必须支持Go的旧版本,并且xerrors不是一个选项。
  2. 您要创建一个唯一的错误,并not包装一个现有错误。例如,如果您在搜索用户时从数据库中收到Not found错误,并且想要将此错误转换为Unauthorized响应,则这可能是适当的。在这种情况下,很少会在any格式化动词上使用原始错误值。

好,%v%s呢?

有关如何实现%s%v的详细信息,请参见in the docs。我已突出显示与您的问题有关的部分。

  1. 如果操作数是一个reflect.Value,操作数将替换为其所持有的具体值,并使用下一条规则继续打印。

  2. 如果操作数实现Formatter接口,它将被调用。 Formatter提供了良好的格式化控制。

  3. 如果%v动词与#标志(%#v)一起使用,并且操作数实现GoStringer接口,则将被调用。

    如果格式(对于Println等而言是隐含的%v。)对于字符串有效(%s%q%v%x%X),以下两个规则适用

  4. 如果操作数实现错误接口,则将调用Error方法将对象转换为字符串,然后将根据动词的要求对其进行格式化(如果有)。

  5. 如果操作数实现方法String()字符串,则将调用该方法将对象转换为字符串,然后将根据动词的要求对其进行格式化(如果有)。

总而言之,fmt.*f功能将:

  1. 寻找Format()方法,如果存在,他们会调用它。
  2. 寻找Error()方法,如果存在,他们会调用它。
  3. 寻找String()方法,如果存在,则调用它。
  4. 使用某些默认格式。

因此,实际上,这意味着%s%v是相同的,除了在错误类型上存在Format()方法时除外。当错误确实具有Format()方法时,可能希望它会与%s%verr.Error()产生相同的输出,但是由于这取决于错误的实现,因此没有错误保证,因此这里没有“正确答案”。


-1
投票

使用%v作为错误值。>>

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

但是,在Go 1.13中,fmt.Errorf函数支持新的%w动词。当存在该动词时,由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)
}

读取:For an error, when should I switch to w


此外,内置的错误接口允许Go程序员添加所需的任何信息。它所需要的只是一种实现Error方法的类型

示例:

type QueryError struct {
    Query string
    Err   error
}

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

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

© www.soinside.com 2019 - 2024. All rights reserved.