How to detect that a pointer is undefined?

With the release version of 0.12.0, pointers set to undefined no longer seem to have a known address. The following prints 0 in 0.11.0 and 0.12.0-dev.3635+786876c05:

const std = @import("std");

const A = struct {
    ptr: *u8 = undefined,
};

pub fn main() void {
    inline for (@typeInfo(A).Struct.fields) |field| {
        if (field.default_value) |opaque_ptr| {
            const ptr_ptr: *const field.type = @ptrCast(@alignCast(opaque_ptr));
            const ptr = ptr_ptr.*;
            std.debug.print("{x}\n", .{@intFromPtr(ptr)});
        }
    }
}

With 0.12.0 I’m getting something like 7ffeee0bd310.

What I’m trying to do is avoid dereferencing the pointer that I’m getting from default_value when it isn’t valid. Is there some new way we’re supposed to do this now? The following code is what I was using:

    const invalid_address = create: {
        const invalid_ptr: *u8 = undefined;
        break :create @intFromPtr(invalid_ptr);
    };
    if (address == invalid_address) {
        // return a struct indicating the lack of valid data
    }

It doesn’t work anymore.

This seems to fix it:

    const invalid_address = create: {
        var invalid_ptr: *u8 = undefined;
        _ = &invalid_ptr;
        break :create @intFromPtr(invalid_ptr);
    };
1 Like

You can’t check if a value is undefined because that means it is in an invalid state that cannot be checked. If you want to check whether a value is populated or not, that’s what optionals are for, and then you can check if it is null.

Ideally, you would never have undefined values. However, sometimes it is simpler to temporarily use undefined values while populating all the fields of a struct.

9 Likes

Hold on, like this

:slight_smile:

So I had exactly the same interrogation yesterday and the documentation was useful (as usual): Documentation - The Zig Programming Language

undefined can be coerced to any type. Once this happens, it is no longer possible to detect that the value is undefined. 
undefined means the value could be anything, even something that is nonsense according to the type. 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." 
3 Likes

Well, you would hear no objection from me if std.builtin.Type.StructField is changed so that a separate is_required flag indicates whether the struct field needs to be initialized while default_value works in the manner you described. It’s awkward as hell having default_value point to bogus data. As least with a pointer you can tell it’s bogus. With an array you can’t tell at all:

const std = @import("std");

const Struct = struct {
    bytes: [1024]u8 = undefined,
};

pub fn main() void {
    inline for (@typeInfo(Struct).Struct.fields) |field| {
        if (field.default_value) |opaque_ptr| {
            const value_ptr: *const field.type = @ptrCast(@alignCast(opaque_ptr));
            const value = value_ptr.*;
            std.debug.print("{any}\n", .{value});
        }
    }
}
{ 0, 0, 0, 0, [...1020 more zeroes...] }

Do you create your instances through generic meta programming?

If you write explicit code to create your instances you know, just from writing the code and looking at the type, that it needs to be set to a useful value. Usually when I use undefined, I have some code somewhere, that acts on a flag to make sure things have been initialized or something similar.

Makes me wonder whether this is a case of going over-board with the meta programming, so you don’t know what your values are, or should be, anymore.

If it is, I guess you maybe could use something like the TAGS pattern to provide extra information at comptime for fields that default to undefined.


Another thing you could consider is whether you can remove the default value and instead treat the whole instance as undefined, until you can set a value to it? (But that depends on what your actual code is).

I basically mean inserting undefined into some data structure and keeping track that, that isn’t initialized yet. Then you are able to get the key/index/pointer for it, pass that on to whatever needs the reference, compute what ever dependent things need computing (for example getting their key/index/pointer) and then using those results to initialize the undefined instance.

Usually when I use undefined it is for something, that only gets partially initialized when the value is created, that is why I suggest the fake creation pattern above as a possible alternative, where you basically create the reference for the value before you create the value itself. However this might need some other helper data structure, that collects partial information until it can be set fully, or to keep track of which parts have been initialized, if it happens in stages.

2 Likes

My project deals with code with written by other programmers. The goal is to cover any data type that can be legally defined in Zig.

1 Like