我想在Go中创建一个链接API的方法。在所有的例子中,我都可以发现链式操作似乎始终是成功的,我无法保证。因此,我尝试扩展这些以添加错误返回值。
如果我这样做
package main
import "fmt"
type Chain struct {
}
func (v *Chain)funA() (*Chain, error ) {
fmt.Println("A")
return v, nil
}
func (v *Chain)funB() (*Chain, error) {
fmt.Println("B")
return v, nil
}
func (v *Chain)funC() (*Chain, error) {
fmt.Println("C")
return v, nil
}
func main() {
fmt.Println("Hello, playground")
c := Chain{}
d, err := c.funA().funB().funC() // line 24
}
编译器告诉我chain-err-test.go:24: multiple-value c.funA() in single-value context
并且不会编译。有没有一个好方法,所以funcA,funcB和funcC可以报告错误并停止该链?
答案 0 :(得分:33)
有没有一个好方法,所以funcA,funcB和funcC可以报告错误并停止该链?
不幸的是,不,你的问题没有很好的解决方案。解决方法非常复杂(添加错误通道等)成本超过增益。
方法链接不是Go中的习惯用法(至少不适用于可能出错的方法)。这不是因为方法链有任何特别的错误,而是返回错误而不是恐慌的习惯用语的结果。其他答案是解决方法,但没有一个是惯用的。
我可以问,在Go中链接方法是不是惯用的,因为我们在Go中返回错误的结果,或者更常见的是多方法返回的结果?
好问题,但不是因为Go支持多次退货。 Python支持多个返回,Java也可以通过Tuple<T1, T2>
类支持;方法链在两种语言中都很常见。这些语言可以逃脱的原因是因为它们通过异常惯性地传达错误。异常立即停止方法链并跳转到相关的异常处理程序。这是Go开发人员通过选择返回错误而特别试图避免的行为。
答案 1 :(得分:3)
您可以这样尝试: https://play.golang.org/p/dVn_DGWt1p_H
package main
import (
"errors"
"fmt"
)
type Chain struct {
err error
}
func (v *Chain) funA() *Chain {
if v.err != nil {
return v
}
fmt.Println("A")
return v
}
func (v *Chain) funB() *Chain {
if v.err != nil {
return v
}
v.err = errors.New("error at funB")
fmt.Println("B")
return v
}
func (v *Chain) funC() *Chain {
if v.err != nil {
return v
}
fmt.Println("C")
return v
}
func main() {
c := Chain{}
d := c.funA().funB().funC()
fmt.Println(d.err)
}
答案 2 :(得分:1)
如果您可以控制代码并且函数签名相同,则可以编写如下内容:
func ChainCall(fns ...func() (*Chain, error)) (err error) {
for _, fn := range fns {
if _, err = fn(); err != nil {
break
}
}
return
}
答案 3 :(得分:1)
您可以通过收集一部分功能来使自己的链条变得懒惰
package main
import (
"fmt"
)
type (
chainFunc func() error
funcsChain struct {
funcs []chainFunc
}
)
func Chain() funcsChain {
return funcsChain{}
}
func (chain funcsChain) Say(s string) funcsChain {
f := func() error {
fmt.Println(s)
return nil
}
return funcsChain{append(chain.funcs, f)}
}
func (chain funcsChain) TryToSay(s string) funcsChain {
f := func() error {
return fmt.Errorf("don't speek golish")
}
return funcsChain{append(chain.funcs, f)}
}
func (chain funcsChain) Execute() (i int, err error) {
for i, f := range chain.funcs {
if err := f(); err != nil {
return i, err
}
}
return -1, nil
}
func main() {
i, err := Chain().
Say("Hello, playground").
TryToSay("go cannot into chains").
Execute()
fmt.Printf("i: %d, err: %s", i, err)
}
答案 4 :(得分:0)
这种方法怎么样:创建一个委托Chain
和error
的结构,并返回它而不是两个值。 e.g:
package main
import "fmt"
type Chain struct {
}
type ChainAndError struct {
*Chain
error
}
func (v *Chain)funA() ChainAndError {
fmt.Println("A")
return ChainAndError{v, nil}
}
func (v *Chain)funB() ChainAndError {
fmt.Println("B")
return ChainAndError{v, nil}
}
func (v *Chain)funC() ChainAndError {
fmt.Println("C")
return ChainAndError{v, nil}
}
func main() {
fmt.Println("Hello, playground")
c := Chain{}
result := c.funA().funB().funC() // line 24
fmt.Println(result.error)
}