Structs y Métodos
## Objetivos de Aprendizaje
- Definir y usar structs
- Crear e inicializar instancias de struct
- Trabajar con campos de struct
- Definir métodos con receptores
- Comprender receptores de valor vs puntero
- Incrustar structs para composición
## Structs
### Definición
```go
type Person struct {
Name string
Age int
City string
}
```
### Declaración
```go
var p Person // Inicializado a cero
p := Person{} // También inicializado a cero
p := Person{Name: "Alice", Age: 30} // Con nombres de campos
p := Person{"Alice", 30, "NYC"} // Posicional (frágil)
```
### Acceso a Campos
```go
p := Person{Name: "Alice", Age: 30}
fmt.Println(p.Name) // Alice
fmt.Println(p.Age) // 30
p.Age = 31 // Modificar campo
fmt.Println(p.Age) // 31
```
## Creando Instancias
### Valor Cero
```go
var p Person // Todos los campos con valor cero
fmt.Println(p) // { 0 }
```
### Literal Compuesto
```go
p := Person{
Name: "Alice",
Age: 30,
}
```
### Operador new
```go
p := new(Person) // Retorna *Person (puntero)
p.Name = "Bob" // No se necesita sintaxis de flecha
fmt.Println(p) // &{Bob 0}
```
### Puntero a Literal
```go
p := &Person{Name: "Carol", Age: 25}
fmt.Println(p) // &{Carol 25}
```
## Struct con Punteros
```go
type Node struct {
Value int
Next *Node
}
n1 := &Node{Value: 1}
n2 := &Node{Value: 2}
n1.Next = n2
fmt.Println(n1.Next.Value) // 2
```
## Métodos
### Receptor de Valor
```go
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func main() {
r := Rectangle{Width: 10, Height: 5}
fmt.Println(r.Area()) // 50
}
```
### Receptor de Puntero
```go
type Rectangle struct {
Width, Height float64
}
func (r *Rectangle) Scale(factor float64) {
r.Width *= factor
r.Height *= factor
}
func main() {
r := Rectangle{Width: 10, Height: 5}
r.Scale(2)
fmt.Println(r.Width, r.Height) // 20 10
}
```
### Cuándo Usar Punteros
| Situación | Recomendación |
|-----------|----------------|
| Modificar el receptor | Usar `*T` |
| Mutar un objeto grande | Usar `*T` |
| Asegurar consistencia | Usar `*T` |
| Método de solo lectura | Usar `T` o `*T` |
## Tipos Nombrados
### Definición de Tipo
```go
type Celsius float64
type Fahrenheit float64
func (c Celsius) ToFahrenheit() Fahrenheit {
return Fahrenheit(c*9/5 + 32)
}
func main() {
var c Celsius = 100
fmt.Printf("%.2f C = %.2f F\n", c, c.ToFahrenheit())
}
```
## Structs Incrustados (Composición)
### Incrustación Básica
```go
type Address struct {
Street string
City string
State string
}
type Person struct {
Name string
Address // Incrustado (sin nombre de campo)
}
func main() {
p := Person{
Name: "Alice",
Address: Address{
Street: "123 Main St",
City: "NYC",
State: "NY",
},
}
fmt.Println(p.Street) // Acceso directo (promovido)
fmt.Println(p.Address.Street) // También funciona
}
```
### Sombreado
```go
type A struct {
x int
}
type B struct {
A
x int // Sombrea A.x
}
func main() {
b := B{A: A{x: 1}, x: 2}
fmt.Println(b.x) // 2 (x de B)
fmt.Println(b.A.x) // 1 (x de A)
}
```
## Tags de Struct
### Definición de Tag de Struct
```go
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
```
### Tags Comunes
```go
type Person struct {
Name string `json:"name" xml:"name"`
Age int `json:"age,omitempty"`
}
```
### Acceder a Tags
```go
import "reflect"
t := reflect.TypeOf(Person{})
field, _ := t.FieldByName("Name")
fmt.Println(field.Tag.Get("json")) // name
```
## Comparando Structs
### Comparables
Los structs son comparables si todos sus campos son comparables.
```go
type Point struct {
X, Y int
}
p1 := Point{1, 2}
p2 := Point{1, 2}
if p1 == p2 {
fmt.Println("Iguales")
}
```
### Con Campos No Comparables
```go
type Person struct {
Name string
Slice []int // No comparable
}
p1 := Person{Name: "Alice"}
p2 := Person{Name: "Alice"}
// p1 == p2 // ERROR DE COMPILACIÓN
```
## Función vs Método
### Función
```go
func area(r Rectangle) float64 {
return r.Width * r.Height
}
```
### Método
```go
func (r Rectangle) area() float64 {
return r.Width * r.Height
}
```
## Diseño de Memoria
### Padding
```go
type Foo struct {
A int8 // 1 byte
_ [7]byte // padding
B int64 // 8 bytes
}
type Bar struct {
B int64 // 8 bytes
A int8 // 1 byte
_ [7]byte // padding
}
```
### El Orden Importa
Poner campos más grandes primero para minimizar padding:
```go
type Efficient struct {
B int64 // 8 bytes
A int8 // 1 byte
C int32 // 4 bytes
}
```
## Patrones Comunes
### Patrón Constructor
```go
type Stack struct {
items []int
}
func NewStack() *Stack {
return &Stack{items: make([]int, 0)}
}
func (s *Stack) Push(item int) {
s.items = append(s.items, item)
}
```
### Patrón Opción
```go
type Server struct {
Port int
Timeout int
}
type Option func(*Server)
func WithPort(port int) Option {
return func(s *Server) {
s.Port = port
}
}
func NewServer(opts ...Option) *Server {
s := &Server{Port: 8080, Timeout: 30}
for _, opt := range opts {
opt(s)
}
return s
}
func main() {
s := NewServer(WithPort(9090))
}
```
## Resumen
- Los structs agrupan campos relacionados
- Usar literales compuestos para inicializar: `Person{Name: "Alice"}`
- `new(Type)` retorna puntero a struct inicializado a cero
- Los métodos tienen receptores - usar receptor de puntero para modificar
- Los structs incrustados proporcionan composición (sin herencia)
- Los tags de struct anotan campos para codificación/decodificación
- El orden de campos de struct afecta el diseño de memoria
- Usar funciones constructor para inicialización compleja
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →