go - unica - MĂșltiples valores en contexto de un solo valor
propuesta de valor ejemplos pdf (5)
Debido al manejo de errores en Go, a menudo termino con múltiples funciones de valores. Hasta ahora, la forma en que he manejado esto ha sido muy desordenada y estoy buscando las mejores prácticas para escribir código más limpio.
Digamos que tengo la siguiente función:
type Item struct {
Value int
Name string
}
func Get(value int) (Item, error) {
// some code
return item, nil
}
¿Cómo puedo asignar una nueva variable al elemento?
item.Value
elegancia.
Antes de introducir el manejo de errores, mi función acaba de devolver un
item
y simplemente podría hacer esto:
val := Get(1).Value
Ahora hago esto:
item, _ := Get(1)
val := item.Value
¿No hay una manera de acceder directamente a la primera variable devuelta?
¿Qué tal este camino?
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]
}
}
En el caso de una función de retorno de varios valores, no puede hacer referencia a campos o métodos de un valor específico del resultado al llamar a la función.
Y si uno de ellos es un
error
, está allí por una
razón
(que es la función que
puede
fallar) y no debe omitirlo porque si lo hace, su código posterior también
podría
fallar miserablemente (por ejemplo, lo que provocaría un pánico en el tiempo de ejecución).
Sin embargo, puede haber situaciones en las que
sepa que
el código no fallará en ninguna circunstancia.
En estos casos, puede proporcionar una función
auxiliar
(o método) que descarte el
error
(o genere un pánico en tiempo de ejecución si aún se produce).
Este puede ser el caso si proporciona los valores de entrada para una función desde el código y sabe que funcionan.
Grandes ejemplos de esto son los paquetes de
template
y
regexp
: si proporciona una plantilla válida o expresiones regulares en tiempo de compilación, puede estar seguro de que siempre se pueden analizar sin errores en tiempo de ejecución.
Por esta razón, el paquete de
template
proporciona la función
Must(t *Template, err error) *Template
y el paquete
regexp
proporciona la función
MustCompile(str string) *Regexp
: no devuelven
error
porque su uso previsto es donde la entrada está garantizado para ser válido.
Ejemplos:
// "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]+/]$`)
De vuelta a su caso
SI
puede estar seguro de que
Get()
no producirá un
error
para ciertos valores de entrada, puede crear una función auxiliar
Must()
que no devolverá el
error
pero generará un pánico en tiempo de ejecución si aún ocurre:
func Must(i Item, err error) Item {
if err != nil {
panic(err)
}
return i
}
Pero no debe usar esto en todos los casos, solo cuando esté seguro de que tiene éxito. Uso:
val := Must(Get(1)).Value
Alternativa / Simplificación
Incluso puede simplificarlo aún más si incorpora la llamada
Get()
en su función auxiliar, llamémosla
MustGet
:
func MustGet(value int) Item {
i, err := Get(value)
if err != nil {
panic(err)
}
return i
}
Uso:
val := MustGet(1).Value
Vea algunas preguntas interesantes / relacionadas:
No, no puede acceder directamente al primer valor.
Supongo que un truco para esto sería devolver una matriz de valores en lugar de "item" y "err", y luego simplemente hacer
item, _ := Get(1)[0]
pero no recomendaría esto.
No, pero eso es bueno, ya que siempre debe manejar sus errores.
Existen técnicas que puede emplear para diferir el manejo de errores, consulte Los errores son valores de 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 }
En este ejemplo de la publicación del blog, ilustra cómo puede crear un tipo
errWriter
que
errWriter
manejo de errores hasta que
errWriter
llamar a
write
.
Sí hay.
Sorprendente, ¿eh?
Puede obtener un valor específico de un retorno múltiple utilizando una función de
mute
simple:
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-
Observe cómo selecciona el número de valor tal como lo haría con un segmento / matriz y luego el tipo para obtener el valor real.
Puedes leer más sobre la ciencia detrás de eso en este artículo . Créditos al autor.