Подтвердить что ты не робот

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

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

Большое спасибо

4b9b3361

Ответ 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-

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

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