एकल-मूल्य संदर्भ में कई मूल्य


110

गो में त्रुटि से निपटने के कारण, मैं अक्सर कई मान कार्यों के साथ समाप्त होता हूं। अब तक, मैंने जिस तरह से इसे प्रबंधित किया है वह बहुत गड़बड़ है और मैं क्लीनर कोड लिखने के लिए सर्वोत्तम प्रथाओं की तलाश कर रहा हूं।

मान लीजिए कि मेरे पास निम्नलिखित कार्य हैं:

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एक त्रुटि के मामले में होगा । पहले एक त्रुटि की जाँच किए बिना, आपका कोड उस स्थिति में क्रैश हो जाएगा।
थॉमस

जवाबों:


83

बहु-मूल्य वापसी फ़ंक्शन के मामले में आप फ़ंक्शन को कॉल करते समय परिणाम के विशिष्ट मूल्य के फ़ील्ड या विधियों का उल्लेख नहीं कर सकते।

और अगर उनमें से एक एक है error, तो यह एक कारण के लिए है (जो फ़ंक्शन विफल हो सकता है) और आपको इसे बाईपास नहीं करना चाहिए क्योंकि यदि आप करते हैं, तो आपका बाद का कोड भी बुरी तरह से विफल हो सकता है (उदाहरण के लिए रनटाइम आतंक)।

हालाँकि ऐसी परिस्थितियाँ हो सकती हैं जहाँ आप जानते हैं कि कोड किसी भी परिस्थिति में विफल नहीं होगा। इन मामलों में आप एक सहायक फ़ंक्शन (या विधि) प्रदान कर सकते हैं जो कि त्याग देगा 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कुछ इनपुट मानों के लिए उत्पादन नहीं होगा , आप एक सहायक 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 में कई रिटर्न पार्स करने के लिए

सामान्य कार्यों पर गोलंग में 'ओके' की तरह वापसी


7

नहीं, लेकिन यह एक अच्छी बात है क्योंकि आपको हमेशा अपनी त्रुटियों को संभालना चाहिए।

ऐसी तकनीकें हैं जिन्हें आप त्रुटि से निपटने में नियोजित कर सकते हैं, देखिए कि रॉब पाइक द्वारा त्रुटियां मान हैं

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, _ := 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]
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.