Prefacio: ¿por qué esta pregunta es tan popular?
\n\nSi alguna vez has escrito en Go aunque sea un par de líneas de código, seguramente te has encontrado con los slices. Son una estructura de datos flexible, potente y probablemente la más utilizada en el lenguaje. Y, como suele suceder, la pregunta más simple —«¿cómo obtener el último elemento?»— resulta ser una de las más frecuentes en Stack Overflow. Miles de desarrolladores, desde principiantes hasta profesionales, buscan esta información. ¿Por qué?
\n\nEn primer lugar, la sintaxis de Go es concisa y la intuición sugiere: «si existe el primer elemento (slice[0]), también debe existir el último». Pero en Go no hay un método .Last() ni slice[-1], como en Python. En segundo lugar, los problemas con slices vacíos y el desbordamiento de índices atrapan incluso a programadores experimentados.
En este tutorial analizaremos todas las formas de obtener el último elemento de un slice en Go, desde la clásica hasta la avanzada, con ejemplos de código y análisis de errores típicos. ¡Allá vamos!
\n\n1. Forma clásica: len(slice) — 1
\n\nEl método más directo y utilizado es acceder al elemento mediante el índice igual a la longitud del slice menos uno. En Go, la indexación comienza desde cero, por lo que si tienes un slice de 5 elementos, sus índices son: 0, 1, 2, 3, 4.
\n\npackage main\n\nimport "fmt"\n\nfunc main() {\n numbers := []int{10, 20, 30, 40, 50}\n last := numbers[len(numbers)-1]\n fmt.Println(last) // Salida: 50\n}\n\n\nFunciona perfectamente. Pero hay un detalle importante: el slice vacío. Si intentas llamar a len(emptySlice)-1 en un slice vacío, la longitud será 0, el índice será -1 y el programa fallará con un pánico index out of range.
var empty []int\n// last := empty[len(empty)-1] // PANIC: runtime error: index out of range [-1]\n\n\nConclusión: Este método es excelente cuando estás seguro de que el slice no está vacío. De lo contrario, sigue leyendo.
\n\n2. Forma segura con verificación de longitud
\n\nPara evitar el pánico, basta con verificar la longitud del slice antes de acceder al último elemento. Este es un enfoque idiomático y seguro que deberías usar en código de producción.
\n\nfunc GetLastElement(s []int) (int, bool) {\n if len(s) == 0 {\n return 0, false // u otro valor por defecto\n }\n return s[len(s)-1], true\n}\n\n\nY aquí un ejemplo con uso en la función principal:
\n\npackage main\n\nimport "fmt"\n\nfunc main() {\n slice1 := []string{"Go", "Python", "Rust"}\n slice2 := []string{}\n\n if last, ok := safeLast(slice1); ok {\n fmt.Println("Último elemento de slice1:", last) // Rust\n }\n\n if _, ok := safeLast(slice2); !ok {\n fmt.Println("slice2 está vacío, no hay último elemento")\n }\n}\n\nfunc safeLast(s []string) (string, bool) {\n if len(s) == 0 {\n return "", false\n }\n return s[len(s)-1], true\n}\n\n\n¿Por qué es bueno esto? La función devuelve dos valores: el elemento en sí y un indicador booleano de éxito. Es un modismo estándar de Go (recuerda la lectura de un map: value, ok := myMap["key"]). El código se vuelve legible y confiable.
3. Uso de reflexión (para universalidad)
\n\nA veces surge la necesidad de escribir una función que funcione con cualquier tipo de slice. Aquí viene al rescate el paquete reflect. Sin embargo, te advierto: es más lento y complejo, así que úsalo solo si realmente es necesario.
package main\n\nimport (\n "fmt"\n "reflect"\n)\n\nfunc LastElement(slice interface{}) (interface{}, bool) {\n v := reflect.ValueOf(slice)\n if v.Kind() != reflect.Slice {\n return nil, false\n }\n if v.Len() == 0 {\n return nil, false\n }\n return v.Index(v.Len() - 1).Interface(), true\n}\n\nfunc main() {\n ints := []int{1, 2, 3}\n strs := []string{"a", "b", "c"}\n\n if last, ok := LastElement(ints); ok {\n fmt