去方法链接和错误处理

时间:2014-12-04 15:26:01

标签: error-handling go chaining

我想在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可以报告错误并停止该链?

5 个答案:

答案 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
}

playground

答案 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)

这种方法怎么样:创建一个委托Chainerror的结构,并返回它而不是两个值。 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)
}