Menu
Coddy logo textTech

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.

challenge icon

Desafío

Fácil

Vamos 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 struct CartItem con los campos Name (string), Price (float64) y Quantity (int). Crea dos funciones que intenten actualizar la cantidad:
    • UpdateQuantityByValue recibe un CartItem (por valor) y una nueva cantidad, luego establece la cantidad del artículo al nuevo valor.
    • UpdateQuantityByPointer recibe un *CartItem (puntero) y una nueva cantidad, luego establece la cantidad del artículo al nuevo valor.
    También crea un método Total en CartItem que 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.97

Observa 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.

Hoja de referencia

Go es un lenguaje de paso por valor. Cuando pasas una variable a una función, Go crea una copia de ese valor, por lo 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 las estructuras (structs). Pasar una estructura a una función copia todos sus campos:

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. El puntero en sí se copia, pero sigue apuntando a la misma dirección de memoria:

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 estructuras grandes para evitar copias costosas. Usa valores cuando quieras proteger los datos originales de cambios no deseados.

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á)
}
quiz iconPonte a prueba

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