Несколько значений в контексте с одним значением
Из-за обработки ошибок в Go я часто оказываю многозначные функции. До сих пор, как я справился с этим, было очень грязно, и я ищу лучшие практики для написания более чистого кода.
Скажем, у меня есть следующая функция:
type Item struct { Value int Name string } func Get(value int) (Item, error) { // some code return item, nil }
Как назначить новую переменную item.Value
элегантно. Прежде чем вводить обработку ошибок, моя функция просто вернула item
и я мог бы просто сделать это:
- Обратный адрес локальной переменной в C
- Вернуть карту, как «ok» в Голанге, при нормальных функциях
- Возвращаемый тип общих методов Java
- Как я могу индексировать массив MATLAB, возвращаемый функцией, не назначая сначала локальную переменную?
- Что должно main () возвращать в C и C ++?
val := Get(1).Value
Теперь я делаю это:
item, _ := Get(1) val := item.Value
Нет ли способа прямого доступа к первой возвращаемой переменной?
- Как сделать метод возвращаемым типом generic?
- Возвращает нулевой плохой дизайн?
- Каков допустимый диапазон для возвращаемого значения программы в Linux / bash?
- Должен ли я возвращать EXIT_SUCCESS или 0 из main ()?
- Получить возвращаемое значение из хранимой процедуры
- Различия между System.out.println () и возвратом в Java
- Почему нам нужно использовать `int main`, а не` void main` в C ++?
- Как вернуть строковое значение из функции Bash
В случае многозначной функции возврата вы не можете ссылаться на поля или методы определенного значения результата при вызове функции.
И если одна из них является 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-
Обратите внимание на то, как вы выбираете номер значения так же, как и из среза / массива, а затем тип, чтобы получить фактическое значение.
Вы можете больше узнать об этой науке из этой статьи . Кредиты автору.