Go中用于“Must”模式的内置助手

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

Built-in helper for "Must" pattern in Go

问题

有没有更多内置的包装器可以使返回(X, error)的函数成功执行或中止,就像regexp.MustCompile一样?

我在说的是像这样的东西,但更“内置”。

英文:

Is there a more built-in wrapper to make a function that returns (X, error) successfully execute or abort, like regexp.MustCompile?

I'm talking about something like this, but more "built-in".

答案1

得分: 6

自Go 1.18起,我们可以定义有类型的Must而不是interface{}

func Must[T any](obj T, err error) T {
	if err != nil {
		panic(err)
	}
	return obj
}

使用方法:https://go.dev/play/p/ajQAjfro0HG

func success() (int, error) {
	return 0, nil
}

func fail() (int, error) {
	return -1, fmt.Errorf("Failed")
}

func main() {
	n1 := Must(success())
	fmt.Println(n1)
	var n2 int = Must(fail())
	fmt.Println(n2)
}

fail()返回非nil的error时,Mustmain函数内失败。

你甚至可以为多于一个返回参数定义Mustn,例如:

func Must2[T1 any, T2 any](obj1 T1, obj2 T2, err error) (T1, T2) {
	if err != nil {
		panic(err)
	}
	return obj1, obj2
}
英文:

Since Go 1.18 we can define typed Must instead of interface{}:

func Must[T any](obj T, err error) T {
	if err != nil {
		panic(err)
	}
	return obj
}

How to use: https://go.dev/play/p/ajQAjfro0HG

func success() (int, error) {
	return 0, nil
}

func fail() (int, error) {
	return -1, fmt.Errorf("Failed")
}

func main() {
	n1 := Must(success())
	fmt.Println(n1)
	var n2 int = Must(fail())
	fmt.Println(n2)
}

Must fails inside main, when fail() returns non-nil error

You can even define Mustn for more than 1 return parameter, e.g.

func Must2[T1 any, T2 any](obj1 T1, obj2 T2, err error) (T1, T2) {
	if err != nil {
		panic(err)
	}
	return obj1, obj2
}

答案2

得分: 3

这是最好的结果:

func Must(fn func() (interface{}, error)) interface{} {
    v, err := fn()
    if err != nil {
        log.Fatalln(err)
    }
    return v
}

然后使用它:

Must(func() (interface{}, error) {
    return template.ParseGlob(pattern)
}).(*template.Template)

假设template.ParseGlob(pattern)是你想要包装的调用。

Go语言没有参数多态性,所以这种代码最终需要使用类型断言来恢复原始类型,而且(在我看来)比它值得的努力更多。对于潜在失败的长链的最整洁、惯用的错误处理方法就是检查错误并返回它。延迟清理处理程序:

func MyFunc() (err error) {
    a, err := blah1()
    if err != nil {
        return
    }
    defer a.Close()
    b, err := blah2(a)
    if err != nil {
        return
    }
    defer b.Close()
    // ad nauseam
}

冗长而乏味,但至少它是明确的并且易于理解。以下是我编写的两个模块,它们迫切需要参数多态性,可能会给你一些处理它的思路:

英文:

There is not. The best you'll get is something like this:

func Must(fn func() (interface{}, error)) interface{} {
    v, err := fn()
    if err != nil {
        log.Fatalln(err)
    }
    return v
}

Then to use it:

Must(func() (interface{}, error) {
    return template.ParseGlob(pattern)
}).(*template.Template)

Assuming that template.ParseGlob(pattern) is the call you wanted to wrap.

Go does not have parametric polymorphism, so this kind of code will end up requiring type assertions to restore the original type and so (in my opinion) is more effort than it's worth. The tidiest, idiomatic error handling you'll get for long chains of potential failure is simply to check for an error, and return it. Defer your cleanup handlers:

func MyFunc() (err error) {
    a, err := blah1()
    if err != nil {
        return
    }
    defer a.Close()
    b, err := blah2(a)
    if err != nil {
        return
    }
    defer b.Close()
    // ad nauseam
}

Long and tedious, but at least it's explicit and easy to follow. Here are two modules I wrote that are crying out for parametric polymorphism that might give you some ideas for dealing without it:

答案3

得分: 2

我不认为内置机制有意义,因为你可以以各种方式处理非nil错误,就像模板包本身的示例所示:参见“text/template/examplefiles_test.go”,展示了两种不同的err用法:

// 这里开始是示例的正文。
// T0.tmpl是第一个匹配的名称,所以它成为起始模板,
// 也是ParseGlob返回的值。
tmpl := template.Must(template.ParseGlob(pattern))

err := tmpl.Execute(os.Stdout, nil)
if err != nil {
	log.Fatalf("template execution: %s", err)
}
// 输出:
// T0调用T1:(T1调用T2:(这是T2))

在特定情况下,辅助函数(*Template) Must()将错误转换为异常(panic)并不总是适用于所有Go程序(如此线程中所讨论的),而要涵盖处理错误的所有可能方式将意味着创建很多“内置”机制。

英文:

I don't think a built-in mechanism would make sense since you could very well handle a non-nil error in various ways, as does the examples in the template package itself: see "text/template/examplefiles_test.go", illustrating 2 different usage of 'err':

// Here starts the example proper.
	// T0.tmpl is the first name matched, so it becomes the starting template,
	// the value returned by ParseGlob.
	tmpl := template.Must(template.ParseGlob(pattern))

	err := tmpl.Execute(os.Stdout, nil)
	if err != nil {
		log.Fatalf("template execution: %s", err)
	}
	// Output:
	// T0 invokes T1: (T1 invokes T2: (This is T2))

In the particular case of the helper function (*Template) Must(), transforming an error into an exception (panic) isn't always the right course for all go programs (as debated in this thread), and to cover all the possible way to handle an error would mean to create a lot of "built-in" mechanisms.

答案4

得分: 0

我自己遇到了同样的问题,并决定开发以下解决方案:https://github.com/boramalper/must

示例:

database := must.MV(sql.Open("sqlite3", "...")).(*sql.DB)
defer must.M(database.Close())

must.M(database.Ping())

// 如果您不关心返回值,请使用MustValVoid(或MVV简称)。
must.MVV(database.Exec(`
    PRAGMA foreign_key_check;
    ...
`))
英文:

I have encountered the same problem myself and decided to develop the following solution: https://github.com/boramalper/must

Example:

database := must.MV(sql.Open("sqlite3", "...")).(*sql.DB)
defer must.M(database.Close())

must.M(database.Ping())

// Use MustValVoid (or MVV shortly) if you don't care about
// the return value.
must.MVV(database.Exec(`
    PRAGMA foreign_key_check;
    ...
`))

答案5

得分: 0

我不确定为什么这里的所有答案都使用了log包,而源代码本身使用的是panic

func MustCompile(str string) *Regexp {
   regexp, err := Compile(str)
   if err != nil {
      panic(`regexp: Compile(` + quote(str) + `): ` + err.Error())
   }
   return regexp
}

我的建议是,不要使用通用的Must包装器,而是根据需要在您的代码中实现Must变体。

  1. https://github.com/golang/go/blob/go1.16.5/src/regexp/regexp.go#L305-L314
英文:

I am not sure why all the answers here are using the log package, when the
source itself uses panic:

func MustCompile(str string) *Regexp {
   regexp, err := Compile(str)
   if err != nil {
      panic(`regexp: Compile(` + quote(str) + `): ` + err.Error())
   }
   return regexp
}

My recommendation would be instead of a generic Must wrapper, just implement
Must variants as needed in your code.

  1. https://github.com/golang/go/blob/go1.16.5/src/regexp/regexp.go#L305-L314

huangapple
  • 本文由 发表于 2012年9月15日 12:42:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/12434565.html
匿名

发表评论

匿名网友

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

确定