🧛‍♀️Errores en Go - Uso de err ≠ nil

No, en Go, nil no es interpretado como 0. El valor nil en Go representa la ausencia de valor o la falta de inicialización para ciertos tipos, como pointers, slices, maps, channels, interfaces, funcs..

Cuando ves una comparación como != nil, no significa que se esté comparando con el número 0, sino que se está verificando si la variable ha sido inicializada o no. Si una variable de alguno de estos tipos tiene el valor nil, significa que no ha sido inicializada correctamente y, por lo tanto, intentar operar con ella causaría un error en tiempo de ejecución.

Ejemplo para map:

var m map[string]int
if m == nil {
    fmt.Println("El mapa no ha sido inicializado.")
}

En este caso, el mapa m está en su estado cero, que es nil, porque no ha sido inicializado con make. Si intentas agregar un valor a m sin inicializarlo, obtendrás un panic.

Err ≠ nil

Cuando ves la expresión err != nil en Go, significa que el programa está verificando si ha ocurrido un error.

En Go, muchas funciones devuelven un valor de error (error), que es una interfaz predefinida en el lenguaje. Si no ha ocurrido ningún error, la función devolverá nil como valor para el error, lo que indica que todo salió bien. Sin embargo, si ocurre un error, la función devolverá un valor que no es nil, y este valor contendrá información sobre el tipo de error que ocurrió.

Por eso, la verificación err != nil es una forma de comprobar si existe un error (si el valor de err no es nil, significa que algo salió mal).

Ejemplo típico:

file, err := os.Open("archivo.txt")
if err != nil {
    fmt.Println("Error al abrir el archivo:", err)
    return // Maneja el error, como devolviendo o terminando la función
}
defer file.Close()

// Si no hubo error, continúa el programa

Resumen:

  • err == nil → No hay error, todo está bien.

  • err != nil → Ocurrió un error, y deberías manejarlo.

La interfaz error()

El tipo error en Go es una interfaz predefinida en el lenguaje, y su definición es la siguiente:

type error interface {
    Error() string
}

Esto significa que cualquier valor que implemente el método Error() que retorne una cadena (string) es considerado un valor de tipo error. Cuando una función devuelve un error, puede ser nil (si no hubo errores) o un valor que implemente Error() (si hubo algún error).

Así que, en resumen:

  • err es solo una convención común para nombrar variables de error.

  • El tipo de err es error, que es una interfaz predefinida en Go.

Err se pasa como parametro o como es que se maneja el error para pasar de segundo?

Ejemplo

file, err := os.Open("archivo.txt")

En este caso, os.Open no recibe dos parámetros. La función Open solo recibe un parámetro: el nombre del archivo como una cadena (string). Lo que pasa es que devuelve dos valores: un descriptor de archivo (file) y un valor de error (err).

En Go, no se pasa un parámetro adicional de tipo error para el manejo de errores, sino que las funciones que pueden fallar devuelven un valor de tipo error como parte de sus retornos. El patrón común es que una función retorne tanto el resultado deseado como un valor de error, y luego tú como desarrollador verificas ese valor de error.

Ejemplo:

func hacerAlgoComplejo() (int, error) {
    // Lógica de la función
    if algoSalioMal {
        return 0, fmt.Errorf("algo salió mal")
    }
    return 42, nil
}
  • Aquí, la función hacerAlgoComplejo devuelve un entero (int) y un error (error).

  • Si algo sale mal dentro de la función, se devuelve un error usando fmt.Errorf, pero no se recibe un parámetro de tipo error.

  • Al llamar a la función, verificas si el error ocurrió:

resultado, err := hacerAlgoComplejo()
if err != nil {
    fmt.Println("Error:", err)
} else {
    fmt.Println("Resultado:", resultado)
}

Conclusión:

Cuando creas funciones complejas, no necesitas pasar un parámetro de tipo error a la función, sino que deberías hacer que la función devuelva un valor de tipo error cuando algo falle. Luego, al usar la función, verificas ese valor de error (err) para asegurarte de que todo esté funcionando correctamente.

Es una buena práctica que tus funciones retornen un valor de tipo error cuando sea necesario y que manejes esos errores de forma explícita en el código que llama a la función.

Preguntas para practicar:

  1. ¿Cuál es la diferencia entre la longitud y la capacidad de un slice?

  2. ¿Qué ocurre cuando modificas un slice que proviene de un array?

  3. Si creas un slice desde un array, ¿modificar el slice afecta el array original? Explica por qué.

  4. Escribe una función que acepte un slice de enteros y devuelva el promedio de esos enteros.

  5. ¿Qué hace la función append() internamente si se excede la capacidad del slice?

Mis respuestas:

1. la longotud es el tamaño total del slice mientras que la capacidad es la capacidad del slice añadir nuevos elementos antes de que necesite redimensionarse de nuevo
2. cuando se modifica un slice que proviene de un array no sucede nada malo ya que el slice es dinamico y puede modificarse a diferencia de un array que no se puede.
3. no lo afecta porque el slice es una nueva copia del array transformado a slice el cual si puede modificar su propia estructura sin probelma alguno, el array seguira tal cual.
4. func main() int{
 slice := []int{1,2,3,4}
leng = len(slice)
for i:=0; i<len(slice); i++{
suma := slice[i-1]+slice[1]
}
return suma/leng
}
5. la funcion append agrega nuevos elementos a un slice y si se excede de la capacidad del slice se retorna un error panic. 
  • Correcto. La longitud (len) es el número de elementos que actualmente contiene el slice, mientras que la capacidad (cap) es el número de elementos que puede contener antes de que Go redimensione el slice.

  • Parcialmente correcto. Modificar un slice afecta al array original si el slice aún está basado en el array subyacente. Los slices no son independientes del array; al contrario, comparten memoria. Así que, si modificas un slice, esos cambios se reflejan en el array original.

  • Incorrecto. Los slices no son copias del array original. Si creas un slice a partir de un array, ambos compartirán la misma memoria. Modificar el slice también modifica el array original, ya que ambos están referenciando la misma región de memoria.

  • Código incorrecto. En tu función, hay algunos problemas:

    • No has inicializado la variable suma.

    • Estás usando índices incorrectos dentro del loop.

    • Además, la función debería devolver float64 porque estamos calculando un promedio.

    Aquí te dejo la corrección:

func promedio(slice []int) float64 {
    suma := 0
    for _, valor := range slice {
        suma += valor
    }
    return float64(suma) / float64(len(slice))
}
  • Incorrecto. Cuando append() excede la capacidad del slice, no retorna un error ni un panic. En lugar de eso, Go crea un nuevo array con mayor capacidad, copia los elementos del array original a este nuevo array, y luego agrega el nuevo elemento. El slice devuelto apunta a este nuevo array.

Last updated