CAPÍTULO 8
Genéricos
Usamos argumentos genéricos cuando queremos crear funciones que puedan aceptar parámetros de distintos tipos. La sintaxis es la siguiente:
fn nombre_func ( T: type, x_a: T , … , ) tipo_devuelto //también puede ser T
o
fn nombre_func ( x_a: anytype , … , ) tipo_devuelto //también puede ser @TypeOf(x_a)
Este tipo de funciones, en realidad, tienen que ver con cómo el compilador de Zig reestructura el código fuente. Cuando definimos una función genérica y luego la usamos , el compilador la trata como una plantilla para generar una versión adaptada para cada tipo que aparece en las llamadas.
Ambas declaraciones especifican que el argumento x_a puede ser de diferentes tipos, según la llamada. Aún así, en el cuerpo de la función, hemos de tener en cuenta que al final operamos con un tipo concreto.
Los parámetros type y anytype son comptime de manera implícita. Si no fuera así el compilador no podría crear las versiones de la misma función en tiempo de compilación. Puedes, pero no necesitas escribir por delante de ellos la palabra comptime, aunque a veces la encontrarás escrita así en algunos tutoriales.
Solo cuando los argumentos no son type o anytype, es necesario poner la palabra comptime en las declaraciones y el compilador te avisará si no lo haces.
El inventor, entre otras cosas, estaba trabajando en una máquina voladora y en un arma nueva para el rey. La máquina voladora -por el riesgo que suponía probarla- seguía a medio hacer, pero el arma funcionaba casi a la perfección: al girar una manivela y soltarla, disparaba decenas de puntas de flecha. Eso sí, no paraba hasta agotar la carga.
load_ammo.zig |
|
const std = @import("std"); const print = std.debug.print; // puntas de flecha de distintos materiales const Proyectile = enum { Wood,Iron,Steel, Copper,Rock, }; // la ametralladora primitiva const MachineGun = struct { a_magazine: [100]Proyectile = undefined, l_ammo: std.ArrayListUnmanaged(Proyectile) = .{}, fn init(self: *MachineGun) void { self.l_ammo = std.ArrayListUnmanaged(Proyectile).initBuffer(&self.a_magazine); } // cargar diferentes cantidades / tipos de munición fn load_ammo(self: *MachineGun, x_ammo: anytype) void { // función anónima solo accesible desde este scope const load_single = struct { fn _(o_me: *MachineGun, x_bullet: anytype) void { if (o_me.l_ammo.capacity == o_me.l_ammo.items.len) print("No hay más sitio\n", .{}) else if (@TypeOf(x_bullet) == Proyectile) o_me.l_ammo.appendAssumeCapacity(x_bullet) else print("¡No puede disparar eso!\n", .{}); } }._; // si es un array cargamos todos los elementos switch (@typeInfo(@TypeOf(x_ammo))) { .array => for (x_ammo) |o_bullet| load_single(self, o_bullet), else => load_single(self, x_ammo), } } // dispara todos los proyectiles fn shoot(self: *MachineGun) void { while (self.l_ammo.items.len > 0) { const o_bullet = self.l_ammo.pop(); print("{any}\n", .{o_bullet}); } } }; pub fn main() !void { var o_mgun = MachineGun{}; o_mgun.init(); // cargar un proyectil o_mgun.load_ammo(Proyectile.Iron); // cargar algo que se encuentra en el suelo o_mgun.load_ammo("Algo que estaba en el suelo"); // cargar una banda de proyectiles o_mgun.load_ammo([_]Proyectile{ .Copper, .Rock, .Steel }); // disparar o_mgun.shoot(); } |
|
$ zig run load_ammo.zig |
|
zig run load_ammo.zig ¡No puede disparar eso! .Steel .Rock .Copper .Iron |