# Pointer capture

**URL:** <https://ziggit.dev/t/pointer-capture/17907>\
**Category:** Explain\
**Created:** [October 10, 2026, 10:08am UTC](https://ziggit.dev/t/pointer-capture/17907 "2026-10-10T10:08:41Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![zeeqeeng](https://ziggit.dev/letter_avatar_proxy/v4/letter/z/8baadc/32.png) [@zeeqeeng](https://ziggit.dev/u/zeeqeeng)\
**Post date:** [October 10, 2026, 10:08am UTC](https://ziggit.dev/t/pointer-capture/17907/1 "2026-10-10T10:08:41Z")

</div>

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](https://ziglang.org/documentation/master/#if-with-Optionals):

```zig
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;

```

---

<div class="post-metadata">

**Author:** ![rpkak](https://ziggit.dev/letter_avatar_proxy/v4/letter/r/a4c791/32.png) [@rpkak](https://ziggit.dev/u/rpkak)\
**Post date:** [October 10, 2026, 10:30am UTC](https://ziggit.dev/t/pointer-capture/17907/2 "2026-10-10T10:30:14Z")

</div>

> [@zeeqeeng](#):
>
> - 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?

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).

---

<div class="post-metadata">

**Author:** ![zeeqeeng](https://ziggit.dev/letter_avatar_proxy/v4/letter/z/8baadc/32.png) [@zeeqeeng](https://ziggit.dev/u/zeeqeeng)\
**Post date:** [October 10, 2026, 10:40am UTC](https://ziggit.dev/t/pointer-capture/17907/3 "2026-10-10T10:40:31Z")

</div>

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?

---

<div class="post-metadata">

**Author:** ![rpkak](https://ziggit.dev/letter_avatar_proxy/v4/letter/r/a4c791/32.png) [@rpkak](https://ziggit.dev/u/rpkak)\
**Post date:** [October 10, 2026, 10:43am UTC](https://ziggit.dev/t/pointer-capture/17907/4 "2026-10-10T10:43:16Z")

</div>

> [@zeeqeeng](#):
>
> it looks like it has type `*u32`

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

---

<div class="post-metadata">

**Author:** ![zeeqeeng](https://ziggit.dev/letter_avatar_proxy/v4/letter/z/8baadc/32.png) [@zeeqeeng](https://ziggit.dev/u/zeeqeeng)\
**Post date:** [October 10, 2026, 10:46am UTC](https://ziggit.dev/t/pointer-capture/17907/5 "2026-10-10T10:46:20Z")

</div>

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

```

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

---

<div class="post-metadata">

**Author:** ![zeeqeeng](https://ziggit.dev/letter_avatar_proxy/v4/letter/z/8baadc/32.png) [@zeeqeeng](https://ziggit.dev/u/zeeqeeng)\
**Post date:** [October 10, 2026, 11:07am UTC](https://ziggit.dev/t/pointer-capture/17907/6 "2026-10-10T11:07:04Z")

</div>

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`?

```zig
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;

```

---

<div class="post-metadata">

**Author:** ![zeeqeeng](https://ziggit.dev/letter_avatar_proxy/v4/letter/z/8baadc/32.png) [@zeeqeeng](https://ziggit.dev/u/zeeqeeng)\
**Post date:** [October 10, 2026, 11:13am UTC](https://ziggit.dev/t/pointer-capture/17907/7 "2026-10-10T11:13:27Z")

</div>

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?

---

<div class="post-metadata">

**Author:** ![vulpesx](https://ziggit.dev/user_avatar/ziggit.dev/vulpesx/32/3989_2.png) [@vulpesx](https://ziggit.dev/u/vulpesx)\
**Post date:** [October 10, 2026, 11:39am UTC](https://ziggit.dev/t/pointer-capture/17907/8 "2026-10-10T11:39:36Z")

</div>

> [@zeeqeeng](#):
>
> 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)
