Checking for Fields with @hasField

for Zig 0.15.2 Buy

Checking for Fields with @hasField

The inventor opened his eyes as the last slap echoed through the entire square.

- Sonno il clockmaker e inventore in the service of la Sua Maestà! - he shouted, realizing he was tied to a post atop a pile of wood and straw. The same inquisitor who had struck him was now lighting a torch.

-For creating inventions of the devil and making a pact with Satan - shouted the inquisitor, torch in hand, turning toward the crowd gathered around. His voice thundered deafeningly in the Plaza de la Cruz Verde, the usual site for executions. The Holy Inquisition does not tolerate pacts with the devil. This is what happens to heretics and sorcerers who practice the dark arts.

-Does not tolerate. Does not tolerate. Stupid bunch of apes - screamed Juanelo, watching as the flame began to consume the straw of the pyre. The inventor started to remember the details. That morning, Manitas was resting in a chair, repairing his own arm, and the inventor was working on the new weapon, when the door burst open with a kick. Moments later, armed men stormed in, dressed in black robes.

-In the name of the Holy Inquisition…- ah, everything bad was always in the name of something, the inventor thought.

They shouted accusations as they tied him up. He tried to explain that he had made no pact, except with the king. Manitas stood up to protect him, but one of the inquisitors struck with such force that his head flew off and landed in a corner. It was then, under the blows, that the inventor also lost consciousness - and now he was trapped in this nightmare.

landing.zig

const std = @import("std");

const print = std.debug.print;

fn get_landing_spot(T: type, x_spot: T) struct { i8, i8, i8 } {

    return .{

        x_spot.n_x,

        x_spot.n_y,

        x_spot.n_z,

    };

}

const Ground = struct { n_x: i8, n_y: i8, n_z: i8, n_area: i8 };

const Stake = struct { n_x: i8, n_y: i8, n_z: i8 };

const Water = struct { n_x: i8, n_y: i8, n_z: i8, n_area: i8 };

const o_juanelo = .{

    .n_x = 4,

    .n_y = 5,

    .n_z = 0,

};

pub fn main() !void {

    const t_landing_spots = .{

        Ground{

            .n_x = 1,

            .n_y = 2,

            .n_z = 0,

            .n_area = 4,

        },

        Ground{

            .n_x = 4,

            .n_y = 4,

            .n_z = 0,

            .n_area = 2,

        },

        Stake{

            .n_x = 4,

            .n_y = 5,

            .n_z = 5,

        },

        Water{

            .n_x = 5,

            .n_y = 1,

            .n_z = 0,

            .n_area = 4,

        },

    };

    var o_land = get_landing_spot(@TypeOf(t_landing_spots[0]), t_landing_spots[0]);

    inline for (t_landing_spots, 0..) |o_spot, n_i| {

        if (n_i == 0) continue;

        //check the distance

        if ((@abs(o_spot.n_x - o_juanelo.n_x) 

            <= @abs(o_land.@"0" - o_juanelo.n_x)) 

           and

            (@abs(o_spot.n_y - o_juanelo.n_y) 

            <= @abs(o_land.@"1" - o_juanelo.n_y)))

              o_land = get_landing_spot(@TypeOf(o_spot), o_spot);

    }

    print("Landing on: {any}\n", .{o_land});

}

$ zig run landing.zig

Landing on: .{ 4, 5, 5 }

Juanelo had just closed his eyes, blinded by the increasingly dense smoke rising beneath his feet… and then, for a moment, he thought lightning had struck him. He knew that kind of thing could happen when a tree or a lone pole stood in open spaces, just like his pyre in the square. Still with his eyes closed, he felt himself flying through the air and, after what seemed like an eternity, still tied to the post, he landed on top of a pile of straw. He opened his eyes and understood what had happened: he was bruised, battered, and dazed-but at least out of the fire, for now. Manitas, the faithful Manitas, had come flying in the ship they hadn’t even properly tested and crashed straight into the post. Probably a miscalculation. He smiled as he saw him crawling out from under the wreckage of the ship. He was limping, with one arm barely hanging by a thread, but alive.

The code in landing.zig processes several possible landing spots within the plaza's coordinates. It selects the one closest to the inventor’s position. The problem is that, while the stake of the pyre is tall, it lacks a proper surface for the flying machine to land on. The system, however, detects it incorrectly as a valid landing height and crashes straight into the post. Luckily, that knocks Juanelo out of the fire... but Manitas takes a bit of a hit.

Compile Error

        To trigger a compile-time error if the struct being analyzed doesn't have the required field, we use @hasField and @compileError:

  fn get_landing_spot(T: type, x_spot: T) struct { i8, i8, i8 } {

    if (!@hasField(T, "n_area"))

        @compileError("The terrain has no surface\n");

    return .{

        x_spot.n_x,

        x_spot.n_y,

        x_spot.n_z,

    };

  }

In this case, when trying to compile, the following message will appear:

 error: The terrain has no surface

When triggering compile-time errors, the program will never run. If what we want instead is to handle this error at runtime, we can use optionals and check the return value of the get_landing_spot function.

landing_ok.zig

const std = @import("std");

const print = std.debug.print;

fn get_landing_spot(T: type, x_spot: T) ?struct { i8, i8, i8 } {

    // if (!@hasField(T, "n_area"))

    // @compileError("The terrain has no surface\n");

    if (!@hasField(T, "n_area"))

        return null;

    return .{

        x_spot.n_x,

        x_spot.n_y,

        x_spot.n_z,

    };

}

const Ground = struct { n_x: i8, n_y: i8, n_z: i8, n_area: i8 };

const Stake = struct { n_x: i8, n_y: i8, n_z: i8 };

const Water = struct { n_x: i8, n_y: i8, n_z: i8, n_area: i8 };

const o_juanelo = .{ .n_x = 4, .n_y = 5, .n_z = 0 };

pub fn main() !void {

    const t_landing_spots = .{

        Ground{ .n_x = 1, .n_y = 2, .n_z = 0, .n_area = 4 },

        Ground{ .n_x = 4, .n_y = 4, .n_z = 0, .n_area = 2 },

        Stake{ .n_x = 4,.n_y = 5, .n_z = 5 },

        Water{ .n_x = 5, .n_y = 1, .n_z = 0, .n_area = 4 },

    };

    var o_land: struct { i8, i8, i8 } = .{ 

      t_landing_spots[0].n_x, 

      t_landing_spots[0].n_y, t_landing_spots[0].n_z };

    inline for (t_landing_spots, 0..) |o_spot, n_i| {

        if (n_i == 0) continue;

        //check distance

        if ((@abs(o_spot.n_x - o_juanelo.n_x) 

             <= @abs(o_land.@"0" - o_juanelo.n_x)) and

            (@abs(o_spot.n_y - o_juanelo.n_y) 

             <= @abs(o_land.@"1" - o_juanelo.n_y)))

        {

            if (get_landing_spot(@TypeOf(o_spot), o_spot)) |o_xspot|

                o_land = o_xspot;

        }

    }

    print("Landing on: {any}\n", .{o_land});

}

$ zig run landing_ok.zig

Landing on: .{ 4, 4, 0 }

If the flying machine had had this kind of control, it wouldn’t have crashed. There’s another danger too - a water surface, which luckily isn’t too close to the inventor’s position, but still should be filtered out.

Let’s see if you can manage to do it by modifying the source code.

Generic Recursion
Generic Structs
© 2025 - 2026 Zen of Zig