Несколько значений в контексте с одним значением

Из-за обработки ошибок в 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 

Нет ли способа прямого доступа к первой возвращаемой переменной?

В случае многозначной функции возврата вы не можете ссылаться на поля или методы определенного значения результата при вызове функции.

И если одна из них является error , она существует по какой-либо причине (которая может не работать в функции), и вы не должны ее обходить, потому что если вы это сделаете, ваш последующий код также может терпеть неудачу (например, в результате паники в режиме исполнения).

Однако могут быть ситуации, когда вы знаете, что код не подведет ни при каких обстоятельствах. В этих случаях вы можете предоставить вспомогательную функцию (или метод), которая будет отбрасывать error (или повышать панику во время выполнения, если она все еще встречается).
Это может иметь место, если вы указываете входные значения для функции из кода, и знаете, что они работают.
Большими примерами этого являются пакеты template и regexp : если вы предоставите допустимый шаблон или регулярное выражение во время компиляции, вы можете быть уверены, что их можно всегда анализировать без ошибок во время выполнения. По этой причине пакет template предоставляет функцию Must(t *Template, err error) *Template а пакет regexp предоставляет MustCompile(str string) *Regexp : они не возвращают error s, потому что их предполагаемое использование – это то, где вход гарантируется.

Примеры:

 // "text" is a valid template, parsing it will not fail var t = template.Must(template.New("name").Parse("text")) // `^[az]+\[[0-9]+\]$` is a valid regexp, always compiles var validID = regexp.MustCompile(`^[az]+\[[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 

Альтернатива / Упрощение

Вы можете даже упростить его, если вы MustGet вызов Get() в свою вспомогательную функцию, назовем его MustGet :

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

Применение:

 val := MustGet(1).Value 

Нет, но это хорошо, так как вы всегда должны обрабатывать свои ошибки.

Существуют методы, которые вы можете использовать для отсрочки обработки ошибок, см. Ошибки – это значения Роб Пайка.

 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 .

Нет, вы не можете напрямую получить доступ к первому значению.

Я предполагаю, что взлом для этого будет возвращать массив значений вместо «item» и «err», а затем просто сделать item, _ := Get(1)[0] но я бы не рекомендовал этого.

Как насчет этого?

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

Да, есть.

Удивительно, а? Вы можете получить определенное значение из множественного возврата с помощью простой функции 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", aB, aC()) } func E() (bool, string) { return false, "F" } func G() (func()(string), bool) { return func() string { return "Hello" }, true } 

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

Обратите внимание на то, как вы выбираете номер значения так же, как и из среза / массива, а затем тип, чтобы получить фактическое значение.

Вы можете больше узнать об этой науке из этой статьи . Кредиты автору.

Давайте будем гением компьютера.