Lección 9 · 9 de 20
Una pila genérica · Go
Cree un tipo genérico Stack[T] en Go con Push, Pop y Len, y úselo con textos y con enteros: el historial de deshacer de un editor.

Los tipos también pueden tener parámetros de tipo. `Stack[T any]` es una pila de elementos de tipo T, guardados en un slice. Stack[string] y Stack[int] son dos tipos distintos, pero los dos salen de un solo código, sin copiar nada ni usar any.
Los métodos repiten el parámetro en el receptor: func (s *Stack[T]) Push(v T). El receptor es un puntero porque Push y Pop modifican s.items; con un valor, cambiarían una copia. Un detalle: en Go los métodos no pueden declarar parámetros de tipo propios, solo usar los del tipo.
Pop saca el último elemento (una pila es LIFO: el último en entrar es el primero en salir) y devuelve dos valores, el elemento y un bool. Con la pila vacía devuelve top sin tocar, que es el valor cero de T ("" para textos, 0 para números), y false. Por eso el cuarto deshacer escribe undo "" ok=false.
var history Stack[string] ya está lista para usar: su slice es nil y append funciona con él. Las ediciones se deshacen al revés de como se hicieron, que es justo lo que se espera de un botón de deshacer.
pages usa la misma pila con enteros, como el historial de un navegador. &Stack[int]{items: ...} la crea con datos iniciales; eso se puede hacer aquí porque items es visible dentro del paquete main, aunque empiece en minúscula.
Código · main.go
package mainimport "fmt"// Stack is a generic type: Stack[string], Stack[int]... one implementation.type Stack[T any] struct{ items []T }func (s *Stack[T]) Push(v T) { s.items = append(s.items, v) }func (s *Stack[T]) Len() int { return len(s.items) }// Pop returns the zero value of T and false when the stack is empty.func (s *Stack[T]) Pop() (top T, ok bool) { if len(s.items) == 0 { return top, false // top still holds the zero value of T } top = s.items[len(s.items)-1] s.items = s.items[:len(s.items)-1] return top, true}func main() { var history Stack[string] // the zero value is ready to use for _, edit := range []string{"type 'Dear Ana'", "bold title", "insert logo"} { history.Push(edit) } fmt.Println("edits in history:", history.Len()) for i := 0; i < 4; i++ { edit, ok := history.Pop() fmt.Printf("undo %q ok=%t\n", edit, ok) } pages := &Stack[int]{items: []int{1, 4, 9}} // the same code, now with ints page, _ := pages.Pop() fmt.Println("back from page", page, "| pages left:", pages.Len())}Salida · go run main.go
edits in history: 3 undo "insert logo" ok=true undo "bold title" ok=true undo "type 'Dear Ana'" ok=true undo "" ok=false back from page 9 | pages left: 2
Para recordar
- Un tipo genérico se declara con
type Stack[T any] struct. - Los métodos usan el parámetro en el receptor:
(s *Stack[T]). var zero To un resultado con nombre dan el valor cero deT.- Devolver
(T, bool)evita valores mágicos con la pila vacía. Stack[string]yStack[int]son tipos distintos con el mismo código.
Ejercicio
Añada un método Peek() (T, bool) que devuelva el elemento de arriba sin sacarlo, y úselo para escribir cuál es la próxima edición que se desharía antes de cada Pop.