web-dev-qa-db-ja.com

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

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

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

88
Spearfisher

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

そして、そのうちの1つがerrorである場合、reason(これは関数mightが失敗する)であり、 notをバイパスすると、後続のコードmightも悲惨に失敗します(たとえば、実行時のパニックになります)。

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

例:

// "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()が特定の入力値に対してerrorを生成しないことが確実な場合、errorを返さずに実行時にパニックを発生させるヘルパーMust()関数を作成できます。

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」のようなマップを返します

66
icza

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

エラー処理を延期するために使用できるテクニックがあります。 エラーは値です 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型を作成する方法を示しています。

7
jmaloney

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

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

5
Antimony

はいあります。

驚いたね単純な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-

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

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

4
Kesarion

この方法はどうですか?

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]
    }
}
3
Jaehoon