Tek değerli bağlamda birden çok değer


110

Go'daki hata işleme nedeniyle, genellikle birden çok değer işleviyle karşılaşıyorum. Şimdiye kadar, bunu yönetme şeklim çok karmaşıktı ve daha temiz kod yazmak için en iyi uygulamaları arıyorum.

Diyelim ki aşağıdaki işleve sahip olduğumu:

type Item struct {
   Value int
   Name string
}

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

  return item, nil
}

Yeni bir değişkeni item.Valuezarif bir şekilde nasıl atayabilirim . Hata işlemeyi tanıtmadan önce, işlevim geri döndü itemve bunu basitçe yapabilirdim:

val := Get(1).Value

Şimdi bunu yapıyorum:

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

İlk döndürülen değişkene doğrudan erişmenin bir yolu yok mu?


3
itemtipik olarak nilbir hata durumunda olacaktır . Önce bir hatayı kontrol etmezseniz, bu durumda kodunuz çökecektir.
Thomas

Yanıtlar:


83

Çok değerli bir dönüş işlevi durumunda, işlevi çağırırken sonucun belirli bir değerinin alanlarına veya yöntemlerine başvuramazsınız.

Ve bunlardan biri bir ise error, bir destekleyen bir doktor nedenle (işlevdir olabilir başarısız) ile gerektiği değil bunu yaparsanız bunun baypas, sonraki kod çünkü olabilir (örneğin çalışma zamanı panik ile sonuçlanan) da sefil başarısız.

Ancak , kodun hiçbir durumda başarısız olmayacağını bildiğiniz durumlar olabilir. Bu durumlarda, bir yardımcı işlev (veya yöntem) sağlayabilirsiniz; bu, bunu ortadan kaldırır error(veya hala devam ederse çalışma zamanı paniği yaratır).
Bir işlev için giriş değerlerini koddan sağlarsanız ve bunların çalıştığını biliyorsanız bu durum söz konusu olabilir.
Bunun harika örnekleri templateve regexppaketleridir: derleme zamanında geçerli bir şablon veya regexp sağlarsanız, bunların çalışma zamanında her zaman hatasız olarak ayrıştırılabileceğinden emin olabilirsiniz. Bu nedenle templatepaket Must(t *Template, err error) *Templateişlevi sağlar ve regexppaket MustCompile(str string) *Regexpişlevi sağlar: geri dönmezlererrors çünkü bunların amaçlanan kullanımı, girdinin geçerli olmasının garanti edildiği yerdir.

Örnekler:

// "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]+\]$`)

Davanıza geri dönün

EĞER belli olabiliyor Get()üretmek olmayacak errorbelirli giriş değerleri için, bir yardımcı oluşturabilir Must()dönmek olmaz işlevini errorama yine oluşursa bir çalışma zamanı panik yükseltmek:

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

Ancak bunu başarılı olacağından emin olduğunuzda her durumda kullanmamalısınız. Kullanım:

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

Alternatif / Basitleştirme

Get()Aramayı yardımcı işlevinize dahil ederseniz daha da basitleştirebilirsiniz , hadi onu arayalım MustGet:

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

Kullanım:

val := MustGet(1).Value

Bazı ilginç / ilgili sorulara bakın:

golang'da birden fazla getiri nasıl ayrıştırılır

Golang'da normal işlevlerde 'tamam' gibi haritaya dön


7

Hayır, ama bu iyi bir şey çünkü her zaman hatalarınızı halletmelisiniz.

Hata işlemeyi ertelemek için kullanabileceğiniz teknikler vardır, bkz. Hatalar Rob Pike'ın değerleridir .

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
}

Blog gönderisindeki bu örnekte, errWritersiz arama bitene kadar hata işlemeyi erteleyen bir türü nasıl oluşturabileceğinizi gösteriyor write.


6

Evet var.

Şaşırtıcı değil mi? Basit bir muteişlevi kullanarak çoklu dönüşten belirli bir değer elde edebilirsiniz :

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-

Tıpkı bir dilim / diziden yaptığınız gibi değer numarasını nasıl seçtiğinizi ve ardından gerçek değeri almak için türü nasıl seçtiğinize dikkat edin.

Bunun arkasındaki bilim hakkında daha fazla bilgiyi bu makaleden okuyabilirsiniz . Yazara kredi.


5

Hayır, ilk değere doğrudan erişemezsiniz.

Sanırım bunun için bir hack "item" ve "err" yerine bir değerler dizisi döndürmek ve sonra sadece yapmak item, _ := Get(1)[0] ama bunu tavsiye etmem.


3

Peki ya bu yol?

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]
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.