🎓 A1Learner
🔍
0 of 55 lessons completed

Zig Type Functions

A type function is a function that works with types at compile time. Zig gives @TypeOf for the type of a value and @typeName for that type as text.

Type names print as plain text

@TypeOf reads the type of each value. @typeName turns that type into a string that print can show.

const std = @import("std");

pub fn main() !void {
    const out = std.io.getStdOut().writer();
    const count: i32 = 42;
    const price: f64 = 2.5;
    const ready: bool = true;
    try out.print("{s}\n", .{@typeName(@TypeOf(count))});
    try out.print("{s}\n", .{@typeName(@TypeOf(price))});
    try out.print("{s}\n", .{@typeName(@TypeOf(ready))});
}

A function can branch on the type of its argument

The label function compares @TypeOf with known types. The matching branch is picked at compile time for each call.

const std = @import("std");

fn label(value: anytype) []const u8 {
    if (@TypeOf(value) == i32) return "int";
    if (@TypeOf(value) == bool) return "bool";
    return "other";
}

pub fn main() !void {
    const out = std.io.getStdOut().writer();
    const count: i32 = 5;
    const price: f64 = 1.5;
    const ready: bool = true;
    try out.print("{s}\n", .{label(count)});
    try out.print("{s}\n", .{label(price)});
    try out.print("{s}\n", .{label(ready)});
}

A new binding can copy the type of a value

@TypeOf(a) can stand where a type name would go. The new binding then has exactly the same type as a.

const std = @import("std");

pub fn main() !void {
    const out = std.io.getStdOut().writer();
    const a: i32 = 10;
    const b: @TypeOf(a) = 32;
    try out.print("{d}\n", .{a + b});
    try out.print("{s}\n", .{@typeName(@TypeOf(b))});
}

Example

const std = @import("std");

fn label(value: anytype) []const u8 {
    if (@TypeOf(value) == i32) return "int";
    if (@TypeOf(value) == bool) return "bool";
    return "other";
}

pub fn main() !void {
    const out = std.io.getStdOut().writer();
    const count: i32 = 8;
    const ratio: f64 = 0.5;
    const ready: bool = false;
    const extra: @TypeOf(count) = 2;
    try out.print("{s}\n", .{@typeName(@TypeOf(count))});
    try out.print("{s}\n", .{@typeName(@TypeOf(ratio))});
    try out.print("{s}\n", .{@typeName(@TypeOf(ready))});
    try out.print("{s}\n", .{label(count)});
    try out.print("{s}\n", .{label(ratio)});
    try out.print("{s}\n", .{label(ready)});
    try out.print("{d}\n", .{count + extra});
}

Key points

  • @TypeOf(x): the type of the value x.
  • @typeName(T): the name of a type as a string.
  • Type check: code can compare @TypeOf(x) with a known type such as i32.
  • Compile-time branch: a branch on a type is decided while the program compiles.
  • Declared type: @TypeOf(a) can name the type for a new binding.

In this example, @typeName prints the type of each of the three values in order. The label function then branches on those same types and returns int, other, and bool. The extra binding copies the type of count with @TypeOf, so adding it to count gives 10.

Note

types are compile-time information, so type checks cost nothing at run time.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

When x is an i32, what does @typeName(@TypeOf(x)) give?