anytype y T: type
In this example, we see how, in the declaration of the load_ammo function, we use x_ammo: anytype:
|
// load different amounts/types of ammo fn load_ammo(self: *MachineGun, x_ammo: anytype) void { |
We use generics to allow adding either a single projectile or several at once, represented as an array.
We could also explicitly define the type as T and use it like this:
|
fn load_ammo(self: *MachineGun, T: type, x_ammo: T) void { |
The limitations of anytype are:
- anytype can only be used in the function declaration; inside the body, we can't use it as the type of a variable.
|
const x_ammo: anytype; <- compile error |
- It can't be used in the declaration as a return type either; we need to use @TypeOf(variable_name) instead:
|
pack_ammo(x_ammo: anytype) @TypeOf(x_ammo); |
- With T: type we explicitly name the type, making it clear what we're trying to use in the call:
|
load_ammo([5]Proyectile, a_ammo); // we know it's an array of 5 |
Inside the function body, it's necessary to check the concrete data type we're receiving in order to operate correctly.
For that, we use @TypeOf to get the value's type, and @typeInfo to inspect its structure and determine whether it's an array.
|
// if it's an array, load all elements switch (@typeInfo(@TypeOf(x_ammo))) { .array => for (x_ammo) |o_bullet| load_single(self, o_bullet), else => load_single(self, x_ammo), } |
@typeInfo returns a union of type std.builtin.Type. In Zig's own source code, in the file builtin.zig, we can find this type:
|
pub const Type = union(enum) { type: void, void: void, bool: void, noreturn: void, int: Int, float: Float, pointer: Pointer, array: Array, ... |
In Chapter 6, we saw that in a union, only one field can be active at a time. Here, we check whether the active field is .array, iterate over the incoming data, and “load” one projectile per iteration.