Lecciones del módulo (2/5)
Métodos: receptor de valor vs de puntero
Un método en Go es una función con un receptor: un especial
parámetro declarado entre func y el nombre del método, que vincula el
método a un tipo. No hay clases: los métodos se pueden definir en
cualquier tipo con nombre declarado en el paquete actual.
Sintaxis
type Person struct {
Name string
}
func (p Person) Greet() string {
return "ciao " + p.Name
}
p := Person{Name: "Ada"}
fmt.Println(p.Greet()) // "ciao Ada"El receptor p Person convierte a Greet en un método de Person. Dentro del
método, p es una variable como cualquier otra.
Receptor de valor versus receptor de puntero
// VALUE receiver: operates on a COPY of the value
func (p Person) NameUpper() string {
return strings.ToUpper(p.Name)
}
// POINTER receiver: operates on the original value, can mutate it
func (p *Person) Rename(n string) {
p.Name = n
}Con un receptor de puntero puedes modificar el receptor y los cambios son visibles fuera del método. Con un receptor de valor no puedes; solo modifique la copia local.
p := Person{Name: "Ada"}
p.Rename("Grace")
fmt.Println(p.Name) // "Grace" — change is visibleCuándo utilizar un receptor de puntero
Reglas prácticas, por orden de prioridad:
- Debes modificar el receptor → receptor de puntero. Obligatorio.
- La estructura es grande (docenas de campos, matrices dentro) → puntero receptor para evitar copias costosas.
- Consistencia: si AL MENOS un método del tipo tiene un receptor de puntero, use un receptor de puntero para los demás también. Una convención muy importante para interfaces (Módulo 6).
- Tipos de "valores" pequeños e inmutables (tiempo.Tiempo, números complejos, pequeños estructuras) → receptor de valor, más legible.
Métodos en tipos que no son estructuras
Puede definir métodos en cualquier tipo NAMED declarado en su paquete:
type Celsius float64
func (c Celsius) ToFahrenheit() float64 {
return float64(c)*9/5 + 32
}
t := Celsius(20)
fmt.Println(t.ToFahrenheit()) // 68No puede definirlos en tipos de otros paquetes (por ejemplo, int, string): debe
Primero cree un tipo con nombre local.
Pruébalo
Agregue un método de cadena Greet() (receptor de valores) a Persona que devuelva 'ciao <Nombre>'.
Mostrar pista
Receptor entre paréntesis ANTES del nombre del método.
Solución disponible después de 3 intentos
Agregue un método Rename (n cadena) con un receptor POINTER que cambia p.Name.
Mostrar pista
Receptor de puntero: `(p *Person)`.
Solución disponible después de 3 intentos
¿Cuándo debería utilizar un receptor de puntero (p *T)?
func (t ?T) M() { ... }Resumen
- Método = función con receptor:
func (r T) Name(...) ... { ... }. - Receptor de valor: opera sobre una copia; Receptor de puntero: funciona sobre el original.
- Go realiza la conversión
T↔*Tautomáticamente en el sitio de la llamada. - Receptor de puntero cuando: mutación + estructura grande + coherencia con otros métodos del tipo.
- Nunca mezcle receptores de valor y puntero del mismo tipo.
- Los métodos van en tipos NAMED en el paquete actual (incluido el no estructurado).