124

Go でのエラー処理により、多くの場合、複数値関数になってしまいます。これまでのところ、これを管理する方法は非常に面倒でした。よりクリーンなコードを作成するためのベスト プラクティスを探しています。

私が次の機能を持っているとしましょう:

type Item struct {
   Value int
   Name string
}

func Get(value int) (Item, error) {
  // some code

  return item, nil
}

item.Value新しい変数をエレガントに割り当てるにはどうすればよいですか。エラー処理を導入する前に、私の関数は返さitemれたばかりで、これを簡単に行うことができました:

val := Get(1).Value

今私はこれを行います:

item, _ := Get(1)
val := item.Value

最初に返された変数に直接アクセスする方法はありませんか?

4

6 に答える 6

89

複数の値を返す関数の場合、関数を呼び出すときに結果の特定の値のフィールドまたはメソッドを参照することはできません。

そして、それらの 1 つが である場合error、それは何らかの理由(関数失敗する可能性がある) があり、それをバイパスしてはなりません。バイパスすると、後続のコードも惨めに失敗する可能性があるためです (たとえば、ランタイム パニックが発生します)。

ただし、どのような状況でもコードが失敗しないことがわかっている場合もあります。このような場合、ヘルパー関数 (またはメソッド) を提供して、それを破棄するerror(または実行時パニックが発生する場合はそれを発生させる) ことができます。
これは、コードから関数の入力値を提供し、それらが機能することがわかっている場合に当てはまります。
これの好例はパッケージtemplateregexpパッケージです。コンパイル時に有効なテンプレートまたは正規表現を提供すると、実行時にエラーなしで常に解析できることを確認できます。このため、templateパッケージはMust(t *Template, err error) *Template関数を提供し、regexpパッケージは関数を提供しMustCompile(str string) *Regexpます: それらは返されませんerror入力が有効であることが保証されている場合に使用するためです。

例:

// "text" is a valid template, parsing it will not fail
var t = template.Must(template.New("name").Parse("text"))

// `^[a-z]+\[[0-9]+\]$` is a valid regexp, always compiles
var validID = regexp.MustCompile(`^[a-z]+\[[0-9]+\]$`)

ケースに戻る

が特定の入力値をGet()生成しないことが確実な場合、 を返さないが、それでも発生する場合に実行時パニックを発生させるヘルパー関数を作成できます。errorMust()error

func Must(i Item, err error) Item {
    if err != nil {
        panic(err)
    }
    return i
}

ただし、すべての場合にこれを使用する必要はありません。成功することが確実な場合にのみ使用してください。使用法:

val := Must(Get(1)).Value

代替/簡素化

呼び出しをヘルパー関数に組み込むと、さらに単純化することもできますGet()。呼び出しましょうMustGet

func MustGet(value int) Item {
    i, err := Get(value)
    if err != nil {
        panic(err)
    }
    return i
}

使用法:

val := MustGet(1).Value

興味深い/関連する質問を参照してください。

golang で複数のリターンを解析する方法

通常の関数で Golang の 'ok' のようなマップを返す

于 2015-01-30T09:49:56.000 に答える
8

はいあります。

驚くべきことですよね?簡単なmute関数を使用して、複数の戻り値から特定の値を取得できます。

package main

import "fmt"
import "strings"

func µ(a ...interface{}) []interface{} {
    return a
}

type A struct {
    B string
    C func()(string)
}

func main() {
    a := A {
        B:strings.TrimSpace(µ(E())[1].(string)),
        C:µ(G())[0].(func()(string)),
    }

    fmt.Printf ("%s says %s\n", a.B, a.C())
}

func E() (bool, string) {
    return false, "F"
}

func G() (func()(string), bool) {
    return func() string { return "Hello" }, true
}

https://play.golang.org/p/IwqmoKwVm-

スライス/配列からの場合と同じように値番号を選択し、次に実際の値を取得するタイプに注意してください。

この記事から、その背後にある科学について詳しく読むことができます。著者へのクレジット。

于 2016-12-21T15:46:57.753 に答える
3

この先どう?

package main

import (
    "fmt"
    "errors"
)

type Item struct {
    Value int
    Name string
}

var items []Item = []Item{{Value:0, Name:"zero"}, 
                        {Value:1, Name:"one"}, 
                        {Value:2, Name:"two"}}

func main() {
    var err error
    v := Get(3, &err).Value
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(v)

}

func Get(value int, err *error) Item {
    if value > (len(items) - 1) {
        *err = errors.New("error")
        return Item{}
    } else {
        return items[value]
    }
}
于 2015-01-30T08:52:07.887 に答える
1

仮定チェックを使用した一般的なヘルパー関数を次に示します。

func assumeNoError(value interface{}, err error) interface{} {
    if err != nil {
        panic("error encountered when none assumed:" + err.Error())
    }
    return value
}

これは として返されるためinterface{}、通常は関数の戻り値の型にキャストする必要があります。

たとえば、OP の例ではGet(1)、 が返されます(Item, error)

item := assumeNoError(Get(1)).(Item)

これを可能にするトリック: 1 つの関数呼び出しから返された複数の値を、複数変数の引数として別の関数に渡すことができます。

特別なケースとして、関数またはメソッド g の戻り値の数が等しく、別の関数またはメソッド f のパラメーターに個別に割り当て可能な場合、呼び出し f(g(parameters_of_g)) は、戻り値をバインドした後に f を呼び出します。 g の f のパラメーターに順番に。


この回答は既存の回答から大きく借用していますが、この形式の単純で一般的なソリューションを提供したものはありませんでした。

于 2020-10-14T04:50:42.980 に答える