← Volver al listado de tecnologías
Structs y Enums en Zig
Structs y Enums
Structs Básicos
const std = @import("std");
const Punto = struct {
x: f64,
y: f64,
};
const Rectangulo = struct {
origen: Punto,
ancho: f64,
alto: f64,
// Método
pub fn area(self: Rectangulo) f64 {
return self.ancho * self.alto;
}
// Método que modifica
pub fn escalar(self: *Rectangulo, factor: f64) void {
self.ancho *= factor;
self.alto *= factor;
}
};
pub fn main() void {
// Crear instancia
const p = Punto{ .x = 10.0, .y = 20.0 };
// Con valores por defecto
var rect = Rectangulo{
.origen = .{ .x = 0, .y = 0 },
.ancho = 100,
.alto = 50,
};
std.debug.print("Área: {d}\n", .{rect.area()});
rect.escalar(2);
std.debug.print("Nueva área: {d}\n", .{rect.area()});
}
Valores por Defecto
const Config = struct {
puerto: u16 = 8080,
host: []const u8 = "localhost",
max_conexiones: u32 = 100,
debug: bool = false,
};
pub fn main() void {
// Usar todos los defaults
const config1 = Config{};
// Sobrescribir algunos
const config2 = Config{
.puerto = 3000,
.debug = true,
};
std.debug.print("Puerto: {d}\n", .{config2.puerto});
}
Structs Anónimos
const std = @import("std");
fn crearPunto(x: f64, y: f64) struct { x: f64, y: f64 } {
return .{ .x = x, .y = y };
}
pub fn main() void {
const p = crearPunto(5.0, 10.0);
std.debug.print("Punto: ({d}, {d})\n", .{ p.x, p.y });
// Tuple (struct sin nombres)
const tupla: struct { i32, []const u8 } = .{ 42, "hola" };
std.debug.print("Valor: {d}, Texto: {s}\n", .{ tupla[0], tupla[1] });
}
Enums
const std = @import("std");
const Color = enum {
rojo,
verde,
azul,
pub fn esCalido(self: Color) bool {
return self == .rojo;
}
};
const DiaSemana = enum(u8) {
lunes = 1,
martes = 2,
miercoles = 3,
jueves = 4,
viernes = 5,
sabado = 6,
domingo = 7,
pub fn esFinDeSemana(self: DiaSemana) bool {
return self == .sabado or self == .domingo;
}
};
pub fn main() void {
const color = Color.verde;
const dia = DiaSemana.sabado;
std.debug.print("Es cálido: {}\n", .{color.esCalido()});
std.debug.print("Es fin de semana: {}\n", .{dia.esFinDeSemana()});
// Convertir a entero
const valor: u8 = @intFromEnum(dia);
std.debug.print("Valor numérico: {d}\n", .{valor});
}
Tagged Unions
const std = @import("std");
const Resultado = union(enum) {
exito: i32,
error_mensaje: []const u8,
pendiente,
pub fn valor(self: Resultado) ?i32 {
return switch (self) {
.exito => |v| v,
else => null,
};
}
};
const Forma = union(enum) {
circulo: f64, // radio
rectangulo: struct { ancho: f64, alto: f64 },
triangulo: struct { base: f64, altura: f64 },
pub fn area(self: Forma) f64 {
return switch (self) {
.circulo => |radio| std.math.pi * radio * radio,
.rectangulo => |r| r.ancho * r.alto,
.triangulo => |t| (t.base * t.altura) / 2,
};
}
};
pub fn main() void {
const r1 = Resultado{ .exito = 42 };
const r2 = Resultado{ .error_mensaje = "Algo salió mal" };
if (r1.valor()) |v| {
std.debug.print("Valor: {d}\n", .{v});
}
const circulo = Forma{ .circulo = 5.0 };
const rect = Forma{ .rectangulo = .{ .ancho = 10, .alto = 5 } };
std.debug.print("Área círculo: {d}\n", .{circulo.area()});
std.debug.print("Área rectángulo: {d}\n", .{rect.area()});
}
Packed Structs
Para control preciso del layout de memoria:
const std = @import("std");
const Flags = packed struct {
activo: bool,
visible: bool,
seleccionado: bool,
_padding: u5 = 0,
};
pub fn main() void {
var flags = Flags{
.activo = true,
.visible = false,
.seleccionado = true,
};
std.debug.print("Tamaño: {d} bytes\n", .{@sizeOf(Flags)}); // 1 byte
// Convertir a entero
const como_byte: u8 = @bitCast(flags);
std.debug.print("Como byte: {b}\n", .{como_byte});
}
Testing de Structs y Enums
const std = @import("std");
const testing = std.testing;
const Punto = struct {
x: f64,
y: f64,
pub fn distancia(self: Punto, otro: Punto) f64 {
const dx = self.x - otro.x;
const dy = self.y - otro.y;
return @sqrt(dx * dx + dy * dy);
}
pub fn mover(self: *Punto, dx: f64, dy: f64) void {
self.x += dx;
self.y += dy;
}
};
const Estado = enum {
activo,
inactivo,
pendiente,
pub fn esTerminal(self: Estado) bool {
return self == .activo or self == .inactivo;
}
};
const Respuesta = union(enum) {
ok: i32,
err: []const u8,
pub fn esOk(self: Respuesta) bool {
return switch (self) {
.ok => true,
.err => false,
};
}
pub fn obtenerValor(self: Respuesta) ?i32 {
return switch (self) {
.ok => |v| v,
.err => null,
};
}
};
test "crear struct" {
const p = Punto{ .x = 3.0, .y = 4.0 };
try testing.expectEqual(@as(f64, 3.0), p.x);
try testing.expectEqual(@as(f64, 4.0), p.y);
}
test "método de struct" {
const p1 = Punto{ .x = 0, .y = 0 };
const p2 = Punto{ .x = 3, .y = 4 };
try testing.expectEqual(@as(f64, 5.0), p1.distancia(p2));
}
test "modificar struct" {
var p = Punto{ .x = 0, .y = 0 };
p.mover(10, 20);
try testing.expectEqual(@as(f64, 10.0), p.x);
try testing.expectEqual(@as(f64, 20.0), p.y);
}
test "enum básico" {
const estado = Estado.activo;
try testing.expect(estado == .activo);
try testing.expect(estado.esTerminal());
}
test "enum no terminal" {
const estado = Estado.pendiente;
try testing.expect(!estado.esTerminal());
}
test "tagged union ok" {
const resp = Respuesta{ .ok = 42 };
try testing.expect(resp.esOk());
try testing.expectEqual(@as(?i32, 42), resp.obtenerValor());
}
test "tagged union error" {
const resp = Respuesta{ .err = "fallo" };
try testing.expect(!resp.esOk());
try testing.expectEqual(@as(?i32, null), resp.obtenerValor());
}
test "switch exhaustivo en union" {
const resp = Respuesta{ .ok = 100 };
const mensaje = switch (resp) {
.ok => |v| if (v > 50) "grande" else "pequeño",
.err => "error",
};
try testing.expectEqualStrings("grande", mensaje);
}
test "struct con valores default" {
const Config = struct {
valor: i32 = 10,
nombre: []const u8 = "default",
};
const c = Config{};
try testing.expectEqual(@as(i32, 10), c.valor);
try testing.expectEqualStrings("default", c.nombre);
}
Ejercicios
- Crea un struct
Personacon nombre, edad y un métodoesMayorDeEdad - Implementa un enum
Operacioncon variantes suma, resta, mult, div y un métodoaplicar - Crea un tagged union
Resultadoque represente éxito con valor o error con código
Resumen
- Structs agrupan datos relacionados con métodos
- Enums definen conjuntos finitos de valores
- Tagged unions combinan enum + datos asociados
- Packed structs dan control preciso del layout
- Switch en unions debe ser exhaustivo