Paso por valor vs referencia
Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy — lección 21 de 107.
Go es un lenguaje de paso por valor. Cuando pasas una variable a una función, Go crea una copia de ese valor. Esto significa que los cambios dentro de la función no afectan a la variable original.
func double(n int) {
n = n * 2
}
func main() {
x := 10
double(x)
fmt.Println(x) // 10 (sin cambios)
}Lo mismo se aplica a los structs. Pasar un struct a una función copia todos sus campos, por lo que las modificaciones dentro de la función se pierden.
type Person struct {
Name string
Age int
}
func birthday(p Person) {
p.Age++
}
func main() {
alice := Person{Name: "Alice", Age: 30}
birthday(alice)
fmt.Println(alice.Age) // 30 (sin cambios)
}Para modificar el valor original, pasa un puntero en su lugar. El puntero en sí se copia, pero sigue apuntando a la misma dirección de memoria, lo que te permite cambiar los datos originales.
func birthday(p *Person) {
p.Age++
}
func main() {
alice := Person{Name: "Alice", Age: 30}
birthday(&alice)
fmt.Println(alice.Age) // 31 (¡modificado!)
}Usa punteros cuando necesites modificar el valor original o al pasar structs grandes para evitar copias costosas. Usa valores cuando quieras proteger los datos originales de cambios no deseados.
Desafío
FácilVamos a construir un sistema de carrito de compras que demuestre la diferencia entre pasar structs por valor frente a pasarlos por puntero. Verás de primera mano cómo solo los parámetros de puntero pueden modificar los datos originales.
Organizarás tu código en dos archivos:
cart.go: Define un structCartItemcon los camposName(string),Price(float64) yQuantity(int). Crea dos funciones que intenten actualizar la cantidad:UpdateQuantityByValuerecibe unCartItem(por valor) y una nueva cantidad, luego establece la cantidad del artículo al nuevo valor.UpdateQuantityByPointerrecibe un*CartItem(puntero) y una nueva cantidad, luego establece la cantidad del artículo al nuevo valor.
TotalenCartItemque devuelva el precio multiplicado por la cantidad.main.go: Lee los detalles del artículo desde la entrada, crea un CartItem y luego demuestra ambos enfoques de actualización. Primero intenta actualizar con la función por valor y muestra el resultado, luego actualiza con la función por puntero y muestra el resultado nuevamente.
Se proporcionarán las siguientes entradas:
- Línea 1: Nombre del artículo
- Línea 2: Precio (float)
- Línea 3: Cantidad inicial (entero)
- Línea 4: Nueva cantidad a establecer (entero)
Formato de salida:
Initial: [Name] x[Quantity] = $[Total]
After value update: [Name] x[Quantity] = $[Total]
After pointer update: [Name] x[Quantity] = $[Total]Por ejemplo, dados Laptop, 999.99, 1 y 3, tu salida debería ser:
Initial: Laptop x1 = $999.99
After value update: Laptop x1 = $999.99
After pointer update: Laptop x3 = $2999.97Observa cómo la cantidad se mantiene en 1 después de la actualización por valor (la función modificó una copia), pero cambia a 3 después de la actualización por puntero (la función modificó el original). Formatea los totales con dos decimales.
Pruébalo tú mismo
package main
import (
"fmt"
)
func main() {
// Leer entrada
var name string
var price float64
var initialQty, newQty int
fmt.Scanln(&name)
fmt.Scanln(&price)
fmt.Scanln(&initialQty)
fmt.Scanln(&newQty)
// TODO: Crear un CartItem con los valores de entrada
// TODO: Imprimir el estado inicial usando el formato:
// fmt.Printf("Initial: %s x%d = $%.2f\n", ...)
// TODO: Intentar actualizar la cantidad usando UpdateQuantityByValue
// Luego imprimir el resultado (notar que la cantidad no cambiará)
// TODO: Actualizar la cantidad usando UpdateQuantityByPointer
// Luego imprimir el resultado (notar que la cantidad cambiará)
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de POO en Go
Archivos externosEspacio de trabajo y módulos de GoPaquetes e importacionesNombres exportados vs. no exportadosIntroducción a la POO en GoStructs como clasesDefinición de métodos en structsReceptores de puntero vs. de valorInicialización de structsFunciones constructorasResumen - Calculadora simple4Interfaces
Introducción a las interfacesImplementación implícitaLa interfaz como contratoInterfaz vacía (any)Type AssertionType SwitchComposición de interfacesInterfaces Stringer y ErrorResumen: Calculadora de figuras7Encapsulamiento
Campos exportados vs. no exportadosEncapsulamiento a nivel de paqueteMétodos Getter y SetterOcultamiento de información en GoResumen - Registros de estudiantes10Genéricos (Go 1.18+)
Introducción a los GenéricosParámetros de tipoRestricciones de tipoStructs genéricosSolución alternativa para métodos genéricosResumen - Colección genérica13Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón Abstract FactoryPatrón ObserverPatrón Strategy2Inmersión profunda en Tipos y Structs
Tipos básicos y compuestosDefiniciones de tipos personalizadosEtiquetas de StructsStructs anónimosStructs anidadosValores cero y por defectoRepaso - Libreta de contactos5Composición sobre herencia
Por qué Go no tiene herenciaConceptos básicos de incrustación de structsPromoción de métodosIncrustación de múltiples structsIncrustación vs AgregaciónShadowing de métodos incrustadosResumen - Jerarquía de empleados8Manejo de Errores y OOP
La interfaz errorTipos de errores personalizadosEnvoltura de errores (fmt.Errorf)Errores centinelaerrors.Is() y errors.As()Panic, Defer y RecoverResumen - Analizador de archivos3Punteros y Memoria
Fundamentos de punteros en GoPunteros a StructsPaso por valor vs referenciaLa función new()Garbage Collection en GoResumen - Constructor de Linked List6Polimorfismo en Go
Polimorfismo mediante interfacesDuck Typing en GoReglas de satisfacción de interfacesColecciones polimórficasInyección de dependenciasResumen - Procesador de pagos9Concurrencia y POO
Fundamentos de GoroutinesChannels y comunicaciónChannels con buffer vs. sin bufferSentencia Selectsync.Mutex y sync.RWMutexsync.WaitGroupDiseño de estructuras Thread-SafeResumen - Worker Pool