Won't Zig in future will face the same problem with undefined in Javascript

Hello, and thank you for this awesome language.

I’m new to Zig, I just started learning the language from the official documents, thus I come across the undefined value in Zig. I have a Javascript background, and in fact I still use it pretty much everyday, in Javascript there was a problem with undefined as a nullable value, to have both null and undefined in the language will result in ambiguity in the language. I’m new to Zig, and I believe there’s an explination for that decission.

1 Like

Welcome @mbougarne !

This is a common question. null and undefined represent two different concepts. null is a well defined value that means “The Null case of an optional value”. Optionals in zig (syntax: ?T) just mean the value is either Type T or Null. When you work with an optional you have to deal with the null path (or assert that it isn’t null with .? syntax)

undefined is a way to create an uninitialized variable. It is not a well defined value. From the docs it says:

Translated into English, undefined means “Not a meaningful value. Using this value would be a bug. The value will be unused, or overwritten before being used.”

When you set something to undefined you are making the commitment that you will either eventually set it to a meaningful value, or never use the variable again.

11 Likes

I welcome to Ziggit!. Both are protection systems. Unlike JavaScript, both null and undefined are treated as illegal behaviors in debug builds. null expresses that there are no values, while undefined is used by the memory sanitizer or compiler to warn or trap when reading or using memory that hasn’t been initialized. In this context, undefined serves as a signal indicating that a variable was deliberately left without a meaningful initial value. In languages like C/C++ which allow leaving a variable uninitialized the easy path is simply to declare a variable without carefully considering its initial value. In Zig, however, you must explicitly use undefined if you want a variable to remain uninitialized.

There are cases when setting a variable to undefined is justified because there isn’t any meaningful value to assign; however, in most C/C++ scenarios, this is merely the result of laziness. Zig enforces explicit behavior by requiring that you mark a variable as undefined if you intend to leave it uninitialized. This approach is beneficial because if you later try to use the uninitialized variable, the compiler will alert you or, at runtime, your program will crash due to the improper read.

Meanwhile, null operates similarly to how optionals are treated: using a value that might not be present is acceptable as part of normal logic. In Zig, it’s acceptable to use optionals, but using undefined is never acceptable unless it’s explicitly intended.

6 Likes

Thank you @Calder-Ty and @pierrelgol The C/C++ mention helped me to see the safety approach that Zig took over the C/C++ approach that in many cases leads to undefined behavior.

int x;
x+=2; // This is not safe and and it doesn't throw 
// an error in the most cases
// We cannot do it in Zig
//we need to init x before use it

Thank you

1 Like

I don’t think this is quite right:

var x: i32 = undefined;
x += 2;
std.debug.print("{d}", .{x});

is possible and not safety checked. The only difference is that you have to use the undefined keyword.

3 Likes

You are welcome, even if Zig did’t emit errors.

const std = @import("std");

pub fn main() !void {
    const a: i32 = undefined;
    const b = a + 2;
    std.debug.print("{d}", .{b});
}

Which it does most of the time.

❯ zig run main.zig
main.zig:5:15: error: use of undefined value here causes undefined behavior
    const b = a + 2;
              ^

the visual cue is enough that it’s easy to narrow it down.

4 Likes

Most C compilers with any reasonable level of warnings enabled will catch this case too:

#include <stdio.h>

int main(void) {
    int a;
    a += 2;
    printf("a: %d\n", a);
}
> gcc -Wall -Werror uninit.c -o uninit
uninit.c: In function ‘main’:
uninit.c:5:7: error: ‘a’ is used uninitialized [-Werror=uninitialized]
    5 |     a += 2;
      |     ~~^~~~
uninit.c:4:9: note: ‘a’ was declared here
    4 |     int a;
      |         ^
cc1: all warnings being treated as errors
5 Likes

I play with it a little bit (the undefined value), and it’s pretty much like C/C++ the only difference that I can see is that Zig makes undefined explicit not implicit as in C, and with -O ReleaseFast it gives x the value 0 in my machine.

I may be wrong again, but it’s up to the developer how they write their Zig code and how they build it.

I think it’s worth mentioning that you can’t check if a variable is undefined in Zig. Accessing the value in any way (including comparison) is illegal. In JS, it’s very common as far as I remember.

2 Likes

This is a coincidence and may change run-to-run.

5 Likes

It is illegal behavior and is caught by Valgrind:

andy@bark ~/tmp> cat test.zig
const std = @import("std");
pub fn main() void {
    var x: i32 = undefined;
    x += 2;
    std.debug.print("{d}", .{x});
}
andy@bark ~/tmp> zig build-exe test.zig -target x86_64-linux
andy@bark ~/tmp> valgrind ./test
==1375503== Memcheck, a memory error detector
==1375503== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==1375503== Using Valgrind-3.23.0 and LibVEX; rerun with -h for copyright info
==1375503== Command: ./test
==1375503== 
==1375503== Conditional jump or move depends on uninitialised value(s)
==1375503==    at 0x1039022: test.main (test.zig:4)
==1375503==    by 0x10389E6: callMain (start.zig:647)
==1375503==    by 0x10389E6: callMainWithArgs (start.zig:616)
==1375503==    by 0x10389E6: start.posixCallMainAndExit (start.zig:571)
==1375503==    by 0x103861D: (below main) (start.zig:271)

However Zig’s safety check currently misses it because 0xaaaaaaaa + 2 does not overflow. Often times safety checks will catch it however if you multiply or if you use it to index a slice.

Related: runtime safety for branching on undefined values and other undefined behavior caused by undefined values · Issue #63 · ziglang/zig · GitHub

8 Likes