Pointer capture

What’s the relationship of the result type between capture expression and conditional expression? Will using pointer capture change that behavior?

Say T is anyerror!?u32, and expression a has result type T. In a if expression where a is passed as conditional expression:

  • if a has value error.BadValue we need to hold it on the else branch, where the capture expression has result type anyerror;
  • If a has value null, the capture expression optional_value has result type ?u32;
  • If a has value 44, the capture expression optional_value still has result type ?u32 and we need to de-optional to get the integer;
  • But what if the capture expression uses pointer, does the capture expression still has result type ?u32 or not because it’s a special use case, then *?u32? But why didn’t it use de-optional in the process to change the optional value?

The code example comes from Zig documentation #if-with-Optionals:

test "pointer capture" {
    var d: anyerror!?u32 = 3;
    if (d) |*optional_value| {
        if (optional_value.*) |*value| {
            value.* = 9;
        }
    } else |_| {
        unreachable;
    }

    if (d) |optional_value| {
        try expectEqual(9, optional_value.?);
    } else |_| {
        unreachable;
    }
}

const expectEqual = @import("std").testing.expectEqual;

I don’t think I totally understand, what you want to say here.

In general: If a pointer capture is allowed, the type of the pointer capture is a pointer to what the type would be, if the capture is a non-pointer capture.

In the code example, optional_value has type *?u32 (the if takes the pointer of d and optional_value is this pointer of d plus maybe an offset). value has type *u32 (the if takes the pointer of optional_value.* (which is optional_value) and value is this pointer of optional_value.* plus maybe an offset).

2 Likes

Since conditional expression (a) has type anyerror!?u32 in a if, I thinks the captured expression (|optional_value|) should has type either anyerror or ?u32 if no pointer captured. When pointer capture used (*optional_value), it looks like it has type *u32. Where’s optional gone?

Why do you think this? optional_value has type *?u32. The optional is not gone.

    if (d) |*optional_value| {
        if (optional_value.*) |*value| {
            value.* = 9;
        }

Maybe value.* = 9? I would expect value.*.? = 9 here.

I added some @TypeOf builtin and I finally found what I’m confused: optional_value has type *?u32, value has type *u32; I guessed ? gone because we didn’t do de-optional. Shouldn’t we dereference first, de-optional second for *?u32?

test "pointer capture" {
    var d: anyerror!?u32 = 3;
    if (d) |*optional_value| {
        try expectEqual(*?u32, @TypeOf(optional_value));
        if (optional_value.*) |*value| {
            try expectEqual(*u32, @TypeOf(value));
            value.* = 9;
        }
    } else |_| {
        unreachable;
    }

    if (d) |optional_value| {
        try expectEqual(9, optional_value.?);
    } else |_| {
        unreachable;
    }
}

const expectEqual = @import("std").testing.expectEqual;

I might got it. The outer if takes a pointer to the expression, the inner if de-optional the value. In other words, you can only do either de-optional or pointer capture at the same time. Am I right here?

Nope, you can do, AND DID, both at the same time in your example
if (optional_value.*) |*value|

What you can’t do is unravel multiple layers of errors/optionals with a single if (or other operation)

2 Likes