Should code that is not called be ignored for errors?

I know that Zig has an extremely excellent lazy compilation system that can perform dead code elimination and other operations, greatly reducing compilation time.
But I have a question here.


const std = @import("std");

pub fn main() !void{
    std.debug.print("test\n", .{});
}

fn m1() void {
    const kkk = std.crypto.kkk;
    _ = kkk;
}

kkk is not a type or anything under std.crypto, it’s just a placeholder I made up for testing.Even so, Zig still compiled and completely ignored the error.And the compiler will only actually report an error when it is really called.


const std = @import("std");

pub fn main() !void{
    std.debug.print("test\n", .{});
    m1();
}

fn m1() void {
    const kkk = std.crypto.kkk;
    _ = kkk;
}

Because this error will appear when you use pub or export, or call it as shown in the code above,I am not sure whether this is a mistake or an intentional design?

For the most part I find I rarely ever am in a situation like this, and when I am, it is easy to rectify, just use it somewhere.

But it is intended as code may only be valid under certian comptime conditions like targets or build flags.

kkk could exist as a real type in some contexts, and your function may only be called in those contexts, at which point eagerly analysing it would give a false error.

To get around that the compiler would need some truly impressive reasoning about your code.

2 Likes

This is intentional design, e.g. without it the following example of OS-dependent code would fail:

const builtin = @import("builtin");
// Configured in the build.zig, could also put a cImport here.
const windows_only = @import("some_module_that_only_exists_on_windows");

pub fn main() void {
    if (builtin.os.tag == .windows) {
        // Thanks to lazy compilation this function is only compiled when on windows
        windowsOnlyFunction();
    }
}

fn windowsOnlyFunction() void {
    const g = windows_only.xxx;
    _ = g;
}
4 Likes

I see! The main thing that confused me about this feature is that I have a habit of writing the function first, trying to compile it, and then integrating it into the whole system for further testing. This feature might make it difficult for me to confirm whether my function is effective, but it is indeed very useful in these scenarios!

Thank you for your explanation!

Yeah, it may make sense to rethink how you approach writing. e.g. one solution could be to follow test-driven development, write a test first that tests the function, then write the function.
Or you can of course just ignore it and come back later when the function is actually used.

3 Likes

Thank you for your suggestion, I will give it a try :slight_smile:

@Booklight12 please Avoid Screenshots of Text/Code

2 Likes

Sorry, I have already changed it to the code version. Thanks for the reminder.

OTH this nudges you towards writing your tests in parallel to the actual code (e.g. the tests are used to ‘activate’ the actual implementation code).

E.g. even when writing C libraries, and without a rigid unit-test philosophy I start with the “outside” of the library - e.g. what would user code calling that library look like. Once I’m happy with that I start writing a small actual example program which then “drivers” the library implementation, and both the library and example program(s) grow together.

I think this “outside => inward” design philosophy automatically results in user-friendly libraries. And once you know what the library API should look like the implementation basically writes itself.

3 Likes