Paréntesis extraños antes del nombre de la función en Go: análisis detallado

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Paréntesis extraños antes del nombre de la función en Go: análisis detallado



Imagine: abre un código ajeno en Go y ve algo que le hace saltar un ojo:



func (u User) Greet() string {    return "Hola, " + u.Name}


Alto. ¿Qué es ese (u User) antes del nombre de la función? En Python para los métodos se usa def greet(self):, en Java — public String greet() dentro de una clase. Y aquí — unos paréntesis, y además con un tipo dentro. ¿Una locura? ¡No, es genial!



Esta construcción es una de las características más potentes de Go que a menudo desconcierta a los principiantes. En realidad, es un receiver (receptor) — la clave para entender la programación orientada a objetos en Go.



En este artículo, miraremos bajo el capó del compilador de Go, analizaremos cómo funcionan estos paréntesis y por qué sin ellos su código parecerá de los años 90. ¿Listo? ¡Vamos allá!



¿Qué es un receiver y para qué sirve?



En Go no hay clases en el sentido clásico. Pero hay estructuras (struct) y métodos. Los paréntesis antes del nombre de la función son precisamente la declaración de un método para un tipo concreto. Técnicamente, esto se llama receiver parameter.



La sintaxis es simple:

func (receiver_name ReceiverType) MethodName(params) return_type {    // cuerpo del método}


Aquí receiver_name es una variable a través de la cual se accede a los campos de la estructura (como self en Python o this en Java). Y ReceiverType es su tipo (generalmente una estructura).



Ejemplo de la vida real:

package main

import "fmt"

type User struct { Name string Age int}

// Método con receiverfunc (u User) SayHello() { fmt.Printf("¡Hola! Soy %s, tengo %d años.", u.Name, u.Age)}

func main() { user := User{Name: "Alicia", Age: 25} user.SayHello() // Salida: ¡Hola! Soy Alicia, tengo 25 años.}


Observe: llamamos al método mediante un punto, como en cualquier lenguaje POO. Pero bajo el capó, Go hace un truco ingenioso.



Value receiver vs Pointer receiver: batalla de titanes



En Go hay dos tipos de receivers: por valor (value receiver) y por puntero (pointer receiver). La diferencia es colosal, y no entender esto es una causa frecuente de errores.



Value receiver

Cuando escribe func (u User) ..., Go copia toda la estructura. El método trabaja con una copia del objeto. Los cambios dentro del método no afectarán al original.



func (u User) Birthday() {    u.Age++ // ¡Esto solo cambiará la copia!}

func main() { user := User{Name: "Bob", Age: 30} user.Birthday() fmt.Println(user.Age) // ¡Sigue siendo 30! ¿Error?}


Este código no dará error de compilación, pero es lógicamente incorrecto. Espera que la edad aumente, pero permanece igual. ¡Bienvenido al mundo del value receiver!



Pointer receiver

Si usa un puntero: func (u *User) ..., el método recibe una referencia al objeto original. Ahora los cambios serán visibles para todos.



func (u *User) Birthday() {    u.Age++ // Modificamos el original}

func main() { user := &User{Name: "Bob", Age: 30} // Creamos un puntero user.Birthday() fmt.Println(user.Age) // ¡31! ¡Funciona!}


Regla general: si el método modifica el estado de la estructura o la estructura es grande (para evitar copias), use pointer receiver. Si el método solo lee datos, use value receiver.



¿Cómo funciona bajo el capó?



Ahora lo más interesante. Cuando el compilador de Go ve:

type Rectangle struct {    Width, Height float64}

func (r Rectangle) Area() float64 { return r.Width * r.Height}


Lo convierte en una función normal, donde el receiver se convierte en el primer argumento:



                
                

Blogs

Book Recommendations