Go: множественное значение в одно значение
Из-за обработки ошибок в 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
Нет ли способа прямого доступа к первой возвращаемой переменной?
Большое спасибо
Ответы
Ответ 1
В случае многозначной функции возврата вы не можете ссылаться на поля или методы определенного значения результата при вызове функции.
И если один из них является error
, он там по какой-либо причине (который может не работать в функции), и вы не должны обходить его, потому что если вы это сделаете, ваш следующий код также может потерпеть неудачу (например, паника).
Однако могут возникнуть ситуации, когда вы знаете, что код не сработает при любых обстоятельствах. В этих случаях вы можете предоставить вспомогательную функцию (или метод), которая будет отбрасывать error
(или повышать панику во время выполнения, если она все еще встречается).
Это может иметь место, если вы предоставляете входные значения для функции из кода, и знаете, что они работают.
Примером этого могут служить пакеты template
и regexp
: если вы предоставляете действительный шаблон или 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]+\]$`)
Вернуться к вашему делу
ЕСЛИ вы можете быть уверены, что 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
Ответ 2
Нет, но это хорошо, так как вы всегда должны обрабатывать свои ошибки.
Существуют методы, которые можно использовать для отсрочки обработки ошибок, см. Ошибки - значения от 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
.
Ответ 3
Нет, вы не можете напрямую получить доступ к первому значению.
Я предполагаю, что взлом для этого будет возвращать массив значений вместо "item" и "err", а затем просто сделать item, _ := Get(1)[0]
но я бы не рекомендовал этого.
Ответ 4
Как насчет этого пути?
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]
}
}
Ответ 5
Да, есть.
Удивительно, да? Вы можете получить определенное значение из множественного возврата с помощью простой функции 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-
Обратите внимание на то, как вы выберете номер значения так же, как и из среза/массива, а затем тип, чтобы получить фактическое значение.
Вы можете больше узнать о науке, стоящей за этим в этой статье. Кредиты автору.