Devolver más de un valor
Enumera los tipos de los resultados entre paréntesis. Devuelve los valores separados por comas y recíbelos de la misma forma.
Las funciones integradas min y max existen desde Go 1.21. Quien llama tiene que recibir todos los resultados: x := minMax(nums) no compila y da assignment mismatch: 1 variable but minMax returns 2 values.
La convención (valor, error)
La razón más común para devolver dos valores es informar de un fallo. Por convención el error va el último, y cuando no es nil los demás resultados se deben tratar como sin sentido.
Salida:
port: 8080
error: port "http": strconv.Atoi: parsing "http": invalid syntax
error: port out of range
Si algo falla, devuelve el valor cero en todos los demás resultados (0, "", nil). El patrón completo, con cómo envolver y comprobar errores, está en la página de manejo de errores.
El idioma comma-ok
Varias operaciones integradas devuelven un segundo bool opcional que dice si el primer valor es real. Por convención la variable se llama ok.
Estas tres formas (búsqueda en un map, aserción de tipo, recepción de un channel) son especiales: devuelven uno o dos valores según a cuántas variables asignes. Tus propias funciones no pueden hacer eso. Una función declarada con dos resultados siempre devuelve dos.
El identificador vacío
_ recibe un valor y lo descarta. Úsalo para los resultados que no necesitas:
_, err := fmt.Sscan(input, &n)
key, _ := parseLine(line)
Ignorar un error con _ compila, así que hazlo solo cuando sepas que la llamada no puede fallar o que el fallo no importa. strings.Builder.WriteString siempre devuelve un error nil, por ejemplo; cerrar un archivo después de escribir en él, no.
Resultados con nombre
Los parámetros de resultado pueden tener nombre. Se declaran al principio de la función, se inicializan con su valor cero, y un return sin valores devuelve lo que contengan.
Los nombres aparecen en la salida de go doc, lo que ayuda cuando dos resultados comparten tipo: (quotient, remainder int) dice más que (int, int).
Un return vacío en una función larga es difícil de leer, porque quien lee tiene que volver atrás para ver qué contienen los resultados. El consejo habitual: pon nombre a los resultados cuando los nombres documenten algo, y aun así escribe return quotient, remainder de forma explícita salvo que la función tenga pocas líneas.
Resultados con nombre y defer
Una función diferida se ejecuta después de que la sentencia return haya asignado los resultados y antes de que quien llama los reciba. Con resultados con nombre, la función diferida puede leerlos y cambiarlos. Así se añade contexto a cualquier error que devuelva una función, o se convierte un panic en un error.
Esto imprime load : empty name y luego <nil>. return errors.New(...) primero asigna a err, y después la función diferida lo envuelve. Más sobre esto en defer.
Sombrear un resultado con nombre
Los resultados con nombre viven en el ámbito más externo de la función. Un := dentro de un bloque anidado crea una variable nueva con el mismo nombre, y un return vacío devuelve entonces la exterior.
func find() (n int, err error) {
if true {
n, err := compute() // new n and err, shadowing the results
_ = n
_ = err
}
return // returns 0, nil
}
El compilador detecta el peor caso: un return vacío dentro del bloque donde un resultado está sombreado falla con result parameter err not in scope at return. No detecta la versión de arriba, donde el return está fuera del bloque. Usa = en lugar de := cuando te refieras a las variables de resultado.
Errores comunes
- Usar el valor cuando err no es nil. Comprueba primero
err. Una función puede devolver basura junto a un error. - Devolver un puntero nil con tipo como error.
var e *MyErr; return edevuelve unerrorque no es nil. Devuelve unnilliteral si todo va bien. - Demasiados resultados. Tres o más resultados de significado dispar son difíciles de usar. Devuelve un struct.
Preguntas frecuentes
¿Cómo se devuelven varios valores desde una función en Go?
Enumera los tipos de los resultados entre paréntesis y devuelve los valores separados por comas: func minMax(xs []int) (int, int) { ...; return lo, hi }. Quien llama los asigna todos a la vez: lo, hi := minMax(nums).
¿Cómo ignoro uno de los valores de retorno en Go?
Asígnalo al identificador vacío _: _, err := strconv.Atoi(s) o n, _ := strconv.Atoi(s). No puedes omitir un valor sin más: el número de variables de la izquierda tiene que coincidir con el número de resultados.
¿Qué son los valores de retorno con nombre en Go?
Parámetros de resultado que tienen nombre, como func split(n int) (half, rest int). Empiezan con su valor cero, se pueden asignar en el cuerpo y un return vacío devuelve sus valores actuales. También permiten que una función diferida cambie lo que recibe quien llama.
¿Debo usar return vacíos (naked returns) en Go?
Solo en funciones cortas. Un return vacío en una función larga esconde lo que se devuelve y facilita los bugs por sombreado. Las guías de estilo de Go recomiendan dar nombre a los resultados para documentarlos y devolver valores explícitos en cualquier cosa de más de unas pocas líneas.