Цепочка метода и обработка ошибок

Я хочу создать API цепочки методов в Go. Во всех примерах я могу найти, что прикованные операции кажутся всегда успешными, что я не могу гарантировать. Поэтому я стараюсь расширить их, чтобы добавить возвращаемое значение ошибки.

Если я сделаю это так,

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 могли сообщать об ошибке и останавливать эту цепочку?

Ответы

Ответ 1

Есть ли хороший способ, чтобы funcA, funcB и funcC могли сообщать об ошибке и останавливать эту цепочку?

К сожалению, нет, нет хорошего решения вашей проблемы. Временные решения достаточно сложны (добавление каналов ошибок и т.д.), Что стоимость превышает коэффициент усиления.

Цепочка метода не является идиомой в Go (по крайней мере, не для методов, которые могут привести к ошибке). Это происходит не потому, что в цепочках методов есть что-то особенно неправильное, а следствие идиомы возврата ошибок вместо паники. Другие ответы являются обходными решениями, но ни один из них не является идиоматическим.

Могу ли я спросить, не так ли идиоматично цеплять методы в Go из-за следствия возврата ошибки, как и в Go, или это в целом следствие того, что у вас есть несколько методов?

Хороший вопрос, но это не потому, что Go поддерживает несколько возвратов. Python поддерживает несколько возвратов, а Java тоже может быть через класс Tuple<T1, T2>; Цепочки методов распространены на обоих языках. Причина, по которой эти языки могут уйти, заключается в том, что они идиоматически сообщают об ошибках через исключения. Исключения немедленно останавливают цепочку методов и переходят к соответствующему обработчику исключений. Это поведение, которое разработчики Go пытались избежать, выбрав вместо этого ошибки.

Ответ 2

Если у вас есть контроль над кодом, и подпись функции идентична, вы можете написать что-то вроде:

func ChainCall(fns ...func() (*Chain, error)) (err error) {
    for _, fn := range fns {
        if _, err = fn(); err != nil {
            break
        }
    }
    return
}

игровая площадка

Ответ 3

Как насчет этого подхода: создайте структуру, которая делегирует Chain и error, и вернет ее вместо двух значений. например:.

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)
}