错误字符串的不变性程度有多大?

huangapple go评论91阅读模式
英文:

To what extent are errors strings guaranteed to not change?

问题

我对Golang的主要问题之一是错误处理基本上是检查字符串(如果我错了,请不要犹豫告诉我)。

在下面的示例中,我试图创建一个目录,但根据问题的类型会有不同的行为。具体来说,如果目录已经存在,我将只是跳过。

package main

import (
	"fmt"
	"os"
)

func main() {
	err := os.Mkdir("test", 0644)
	if err != nil {
		fmt.Printf("error: %v", err)
		if err.Error() == "mkdir test: Cannot create a file when that file already exists" {
			fmt.Printf("the dir already exists")
		} else {
			panic(err)
		}
	}
}

它不起作用,重复的尝试不会被记录。为什么?啊,糟糕,我忘记了mkdir test: Cannot create a file when that file already exists字符串末尾的句号。

我觉得依赖错误字符串是脆弱的,而不是像err.ErrorType().DirectoryExists()这样的检查(有时在net中存在)。

我的问题是:我在多大程度上可以依赖错误字符串不会改变?(换句话说,mkdir test: Cannot create a file when that file already exists.不会以不同的措辞出现,或者转换成另一种语言等等。)

我对errors.Is()抱有一些希望,但它最终依赖于字符串比较。

英文:

One of the main issues I have with Golang is that the error handling is basically a check for a string (I would love to be wrong, do not hesitate :))

In the example below, I am trying to create a directory, but will have different behaviour depending on the kind of issue. Specifically, if a directory exists I will just pass.

package main

import (
	"fmt"
	"os"
)

func main() {
	err := os.Mkdir("test", 0644)
	if err != nil {
		fmt.Printf("error: %v", err)
		if err.Error() == "mkdir test: Cannot create a file when that file already exists" {
			fmt.Printf("the dir already exists")
		} else {
			panic(err)
		}
	}
}

It does not work, repeated attempts are not logged. Why? Ah, crap, I forgot the dot at the end of the mkdir test: Cannot create a file when that file already exists string.

I feel that relying on an error string is fragile, as opposed to having something like err.ErrorType().DirectoryExists() kind of check (which sometimes exists, in net for instance).

My question: to what extent can I rely on the fact that the error strings will not change? (in other words, that mkdir test: Cannot create a file when that file already exists. will not be worded differently, or ported to another national language, etc.)

I had some hope with errors.Is() but it ultimately relies on the string comparison.

答案1

得分: 6

Go的错误字符串不会随意更改,但也不受Go兼容性政策的覆盖:如果增加了清晰度的好处超过了破坏那些对字符串内容做出(脆弱、不支持的)假设的程序的(不可避免的)代价,那么可以进行更改。

errors包是检查特定类型错误的可靠方式。

使用errors.Is来检查是否与规范错误相等(https://play.golang.org/p/co6ukgQrr58):

	err := os.Mkdir(dir, 0644)
	if errors.Is(err, os.ErrExist) {
		t.Logf("目录已经存在")
	} else if err != nil {
		t.Fatal(err)
	}

使用errors.As来检查特定类型的错误(https://play.golang.org/p/UR1nUCRMUY6):

	err := os.Mkdir(dir, 0644)
	var pe *os.PathError
	if errors.As(err, &pe) {
		t.Logf("创建 %q 时出错:%v", pe.Path, pe.Err)
	} else if err != nil {
		t.Fatal(err)
	}
英文:

Go error strings don't change arbitrarily, but they also aren't covered by the Go compatibility policy: they can be changed if the increase in clarity outweighs the (inevitable) cost of breaking programs that make (fragile, unsupported) assumptions about the string contents.

The errors package is the robust way to check for specific types of errors.

Use errors.Is to check for equivalence to a canonical error (https://play.golang.org/p/co6ukgQrr58):

	err := os.Mkdir(dir, 0644)
	if errors.Is(err, os.ErrExist) {
		t.Logf("the dir already exists")
	} else if err != nil {
		t.Fatal(err)
	}

Use errors.As to check for a particular type of error (https://play.golang.org/p/UR1nUCRMUY6):

	err := os.Mkdir(dir, 0644)
	var pe *os.PathError
	if errors.As(err, &pe) {
		t.Logf("error creating %q: %v", pe.Path, pe.Err)
	} else if err != nil {
		t.Fatal(err)
	}

答案2

得分: 4

在这种情况下,你可以使用os.IsExist(err)

err := os.Mkdir("test", 0644)
if err != nil {
    if os.IsExist(err){
        fmt.Printf("the dir already exists")
    } else {
        panic(err)
    }
}

好的库应该允许你检查错误,而不依赖于字符串比较。有多种方法可以实现:

  • 使用特定值进行比较:if err == os.EOF
  • 使用实用函数:os.IsExist(err)
  • 类型断言:pathErr := err.(*os.PathError)

在标准库中,总是有一种方法可以检查错误,而不依赖于字符串。请查阅函数/包的文档以了解详细信息。

注意:

errors.Is()errors.As() 是对于可能包含其他错误的错误的(较新的)泛化版本,类似于 == 和类型断言。请参阅 https://go.dev/blog/go1.13-errors

英文:

In this case, you can use os.IsExist(err)

err := os.Mkdir("test", 0644)
if err != nil {
    if os.IsExist(err){
        fmt.Printf("the dir already exists")
    } else {
        panic(err)
    }
}

Good libraries should allow you to inspect errors without relying on string comparison. Various methods exist to do so:

  • Comparaison with sentinel values if err == os.EOF
  • Utility function: os.IsExist(err)
  • Type assertion: pathErr := err.(*os.PathError)

There is always a way to inspect errors in the standard library without relying on strings. Check the function/package documentation for details about how to do it.

Note:

errors.Is() and errors.As() are a (~recent) generalisation of == and type assertion but for errors that could contain other errors. See https://go.dev/blog/go1.13-errors

答案3

得分: 0

从https://pkg.go.dev/os#Mkdir:

Mkdir函数用指定的名称和权限位(在umask之前)创建一个新目录。如果出现错误,它将是*PathError类型。

这意味着你可以类型断言返回的错误以获取更多信息。

if err != nil {
    pathErr := err.(*os.PathError)
}

对于来自os包中的函数返回的错误,还要注意这两个函数:

在多大程度上可以依赖于错误字符串不会改变?

在函数的合同所保证的范围内,这通常是通过函数上方的文档注释来写明的,就像大多数编程语言一样。对于os.MkDir()来说,你不能依赖于错误字符串不会改变。

英文:

From https://pkg.go.dev/os#Mkdir:

> Mkdir creates a new directory with the specified name and permission bits (before umask). If there is an error, it will be of type *PathError.

This means you could type-assert the returned error to get more information.

if err != nil {
    pathErr := err.(*os.PathError)
}

With errors returned from functions in package os specifically, also take note of these two functions:

> to what extent can I rely on the fact that the error strings will not change?

To the extent which is guaranteed by the function's contract, which as in most programming languages conventionally is written in documenting comments above the function. In the case of os.MkDir(): you cannot.

huangapple
  • 本文由 发表于 2021年9月8日 21:02:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/69103626.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定