単一値コンテキストの複数の値


110

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

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


3
item通常nil、エラーが発生します。最初にエラーをチェックしないと、コードはその場合クラッシュします。
Thomas

回答:


83

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

そして、そのうちの一つがある場合error、それはそこだ理由(機能があるかもしれない失敗)、あなたはすべきではない、あなたがしなければ、それバイパス、あなたのその後のコードがあるためかもしれません(例えば、ランタイムパニックになる)も無残に失敗します。

ただし、コードがどのような状況でも失敗しないことがわかっている場合があります。これらの場合、あなたはそれを破棄するヘルパー関数(またはメソッド)を提供することができます(errorそれでも実行時にパニックが発生する場合)。
これは、コードから関数の入力値を提供し、それらが機能することがわかっている場合に当てはまります。
これのすばらしい例はtemplateおよびregexpパッケージです。コンパイル時に有効なテンプレートまたは正規表現を指定すると、実行時にそれらを常にエラーなしで解析できることが保証されます。このため、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]+\]$`)

あなたのケースに戻る

IFあなたが確信できるGet()生成されませんerror、特定の入力値のために、あなたはヘルパーを作成することができますMust()返さないだろう機能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」のようなマップを返す


7

いいえ。ただし、常にエラーを処理する必要があるため、これは良いことです。

エラー処理を延期するために使用できる手法があります。エラーは Rob Pikeによるです。

ew := &errWriter{w: fd}
ew.write(p0[a:b])
ew.write(p1[c:d])
ew.write(p2[e:f])
// and so on
if ew.err != nil {
    return ew.err
}

このブログ投稿の例では、errWriter呼び出しが完了するまでエラー処理を遅らせる型を作成する方法を示していますwrite


6

はいあります。

びっくりした?単純な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-

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

その背後にある科学について詳しくは、この記事をご覧ください。著者へのクレジット。


5

いいえ、最初の値に直接アクセスすることはできません。

これのハックは、 "item"と "err"の代わりに値の配列を返すことitem, _ := Get(1)[0] だと思いますが、それを行うだけです が、これはお勧めしません。


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]
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.