# Typing \`: \[1024\]u8 = undefined\` Less?

**URL:** <https://ziggit.dev/t/typing-1024-u8-undefined-less/13887>\
**Category:** Brainstorming\
**Tags:** language\
**Created:** [January 10, 2026, 3:33am UTC](https://ziggit.dev/t/typing-1024-u8-undefined-less/13887 "2026-01-10T03:33:00Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![jdevries3133](https://ziggit.dev/user_avatar/ziggit.dev/jdevries3133/32/6778_2.png) [@jdevries3133](https://ziggit.dev/u/jdevries3133)\
**Post date:** [January 10, 2026, 3:33am UTC](https://ziggit.dev/t/typing-1024-u8-undefined-less/13887/1 "2026-01-10T03:33:00Z")

</div>

I wonder whether inline functions can sort of take the place of macros? After all, `fn buf` isn’t really a frame on the callstack. Presumably post-inlining this could be legit and not invalid memory? I currently get a, “error: returning address of expired local variable,” if I try this. I’m unsure of whether this is a useful idea or if I should just have some more practice sessions of typing 1024 without typos 🙂

```zig
inline fn buf() []u8 {
    var b: [1024]u8 = undefined;
    return &b;
}

```

---

<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:** [January 10, 2026, 4:01am UTC](https://ziggit.dev/t/typing-1024-u8-undefined-less/13887/2 "2026-01-10T04:01:17Z")

</div>

Currently, you can, but you are not intended to be able to reference variables or stack memory outside the block it was defined in, inline functions semantically have their own scope still so are not an exception.

Inline functions are still _semantically_ a function call, the biggest thing to know is all their parameters will still have to be evaluated before it can be called, regardless of what the function does, regardless of the fact that there won’t be a call in the generated machine code.

People tend to get bitten by those when they try to use inline functions to replace macros.

---

<div class="post-metadata">

**Author:** ![jdevries3133](https://ziggit.dev/user_avatar/ziggit.dev/jdevries3133/32/6778_2.png) [@jdevries3133](https://ziggit.dev/u/jdevries3133)\
**Post date:** [January 10, 2026, 4:14am UTC](https://ziggit.dev/t/typing-1024-u8-undefined-less/13887/3 "2026-01-10T04:14:07Z")

</div>

> [@vulpesx](#):
>
> you are not intended to be able to reference variables or stack memory outside the block it was defined in

This makes sense as a rule.

Actually, I gave this some thought and aliasing the `[1024]u8` type to e.g, `Buf` solves my issue here. Example: [feat: alias for Buf · jdevries3133/libj@f07d223 · GitHub](https://github.com/jdevries3133/libj/commit/f07d223e2e253ef8f157105cc13b3d35217c478b).

---

<div class="post-metadata">

**Author:** ![andrewrk](https://ziggit.dev/user_avatar/ziggit.dev/andrewrk/32/7011_2.png) [@andrewrk](https://ziggit.dev/u/andrewrk)\
**Post date:** [January 10, 2026, 4:16am UTC](https://ziggit.dev/t/typing-1024-u8-undefined-less/13887/4 "2026-01-10T04:16:52Z")

</div>

since ba137783ed4152ebe8d7b98dca855c551fc924a7 (Sep 23, 2025):

```zig
$ cat test.zig
inline fn buf() []u8 {
    var b: [1024]u8 = undefined;
    return &b;
}
$ zig ast-check test.zig
test.zig:3:13: error: returning address of expired local variable 'b'
    return &b;
            ^
test.zig:2:9: note: declared runtime-known here
    var b: [1024]u8 = undefined;
        ^

```

---

<div class="post-metadata">

**Author:** ![jdevries3133](https://ziggit.dev/user_avatar/ziggit.dev/jdevries3133/32/6778_2.png) [@jdevries3133](https://ziggit.dev/u/jdevries3133)\
**Post date:** [January 10, 2026, 4:18am UTC](https://ziggit.dev/t/typing-1024-u8-undefined-less/13887/5 "2026-01-10T04:18:15Z")

</div>

The safety check is much appreciated & definitely helped me as I was getting started, so I think it’s a great add!

---

<div class="post-metadata">

**Author:** ![floooh](https://ziggit.dev/user_avatar/ziggit.dev/floooh/32/2989_2.png) [@floooh](https://ziggit.dev/u/floooh)\
**Post date:** [January 10, 2026, 11:49am UTC](https://ziggit.dev/t/typing-1024-u8-undefined-less/13887/6 "2026-01-10T11:49:03Z")

</div>

> since ba137783ed4152ebe8d7b98dca855c551fc924a7 (Sep 23, 2025):

Woah, I totally missed this, kudos! Probably explains why there are fewer help-posts about returning stack memory on here 😉

---

<div class="post-metadata">

**Author:** ![mnemnion](https://ziggit.dev/user_avatar/ziggit.dev/mnemnion/32/2478_2.png) [@mnemnion](https://ziggit.dev/u/mnemnion)\
**Post date:** [January 10, 2026, 2:42pm UTC](https://ziggit.dev/t/typing-1024-u8-undefined-less/13887/7 "2026-01-10T14:42:17Z")

</div>

> [@jdevries3133](#):
>
> aliasing the `[1024]u8` type to e.g, `Buf` solves my issue here. Example: [feat: alias for Buf · jdevries3133/libj@f07d223 · GitHub](https://github.com/jdevries3133/libj/commit/f07d223e2e253ef8f157105cc13b3d35217c478b).

The relationship between arrays and slices takes awhile to click for new Zig enjoyers, so I’m going to explain in depth why this works, I suspect you know all this.

Why this works is that you’re returning an array: `[1024]u8`, and not a slice: `[]u8`. Aliasing it to `Buf` is just a convenience.

A slice `[]` is a pointer with a length, so the result location is two words in size. An array `[1024]` is the memory a slice can point to, in this case 1024 bytes in size.

The original inline function was creating the array inside the function body and returning a pointer to it. This _could be legal_, because `inline` is semantic in Zig, it’s not a hint the compiler can overlook, it has effects on (in particular) comptime analysis. We had a [long discussion about this](https://ziggit.dev/t/what-makes-ban-returning-pointer-to-stack-memory-difficult/9380) awhile ago. It wouldn’t result in a dangling stack pointer because the stack is not permitted to be pushed and popped for an `inline` fn call.

But I think this is the correct decision. It means that the mental model of the function’s call stack is everything visible in the function definition. An inline function is not so-marked _at the call site_, and we want reasoning about code to be as local as possible.

This is a good illustration of why that’s fine the way it is, because your inline function can just return an array, `[1024]u8`, and it _should_ do that, since that’s the type you want.

Say `1024` isn’t always the number, no problem!

```zig
pub inline fn buf(len: comptime_int) [len]u8 {
    var b: [len]u8 = undefined;
    return b;
}

```

Now it’s `buf(1024)`, `buf(4096)`, whatever.

Last note: `inline` is not needed for correctness of this function. I would use it, personally, because I don’t want to rely on result location analysis to ensure one `buf` rather than two are created. But in Zig, the length of an array is part of the type itself, the result location has room for it, you can always return an array.

Zig doesn’t (and shouldn’t\[1\]) allow runtime-variable amounts of stack space to be created, and how inline functions deal with dangling memory wouldn’t change that. Therefore there’s never any need: just return the memory, not a pointer to it. If and when variable amounts of memory are needed, we use the heap for that.

* * *

1. [alloca](https://www.man7.org/linux/man-pages/man3/alloca.3.html) is a security nightmare, is why.

---

<div class="post-metadata">

**Author:** ![invlpg](https://ziggit.dev/letter_avatar_proxy/v4/letter/i/13edae/32.png) [@invlpg](https://ziggit.dev/u/invlpg)\
**Post date:** [January 10, 2026, 3:14pm UTC](https://ziggit.dev/t/typing-1024-u8-undefined-less/13887/8 "2026-01-10T15:14:35Z")

</div>

> [@mnemnion](#):
>
> This is a good illustration of why that’s fine the way it is, because your inline function can just return an array, `[1024]u8`, and it _should_ do that, since that’s the type you want.
> 
> Say `1024` isn’t always the number, no problem!

That doesn’t really help things, because unless you assign that array to a local variable, you _cannot coerce a pointer to it into a mutable slice to be passed to writer/reader_. (Note that it also doesn’t compile unless you changed `var b` to `const b`).

You’ve just made it so that instead of writing

```zig
var buffer: [1024]u8 = undefined;

```

It becomes:

```zig
var buffer = buf(1024);

```

Which isn’t much of an improvement in my opinion.

---

<div class="post-metadata">

**Author:** ![mnemnion](https://ziggit.dev/user_avatar/ziggit.dev/mnemnion/32/2478_2.png) [@mnemnion](https://ziggit.dev/u/mnemnion)\
**Post date:** [January 10, 2026, 3:15pm UTC](https://ziggit.dev/t/typing-1024-u8-undefined-less/13887/9 "2026-01-10T15:15:32Z")

</div>

> [@invlpg](#):
>
> You’ve just made it so that instead of writing
> 
> ```zig
> var buffer: [1024]u8 = undefined;
> 
> ```
> 
> It becomes:
> 
> ```zig
> var buffer = buf(1024);
> 
> ```

Correct.

> [@invlpg](#):
>
> Which isn’t much of an improvement in my opinion.

There is a Dude quote which comes to mind…

---

<div class="post-metadata">

**Author:** ![Zambyte](https://ziggit.dev/user_avatar/ziggit.dev/zambyte/32/4977_2.png) [@Zambyte](https://ziggit.dev/u/Zambyte)\
**Post date:** [January 10, 2026, 3:48pm UTC](https://ziggit.dev/t/typing-1024-u8-undefined-less/13887/10 "2026-01-10T15:48:24Z")

</div>

> [@mnemnion](#):
>
> A slice `[]` is a pointer with a length, so the result location is two words in size. An array `[1024]` is the memory a slice can point to, in this case 1024 bytes in size.

I think this is a helpful way to think of slices:

```zig
[]u8 == struct { len: usize, ptr: [*]u8 }

```

Which can be demonstrated with:

```zig
@compileLog(@sizeOf([]u8)); // prints @as(comptime_int, 16) at compile time
@compileLog(@sizeOf(struct { len: usize, ptr: [*]u8 })); // prints @as(comptime_int, 16) at compile time
@compileLog(@sizeOf([1024]u8)); // prints @as(comptime_int, 1024) at compile time

```

---

<div class="post-metadata">

**Author:** ![IntegratedQuantum](https://ziggit.dev/user_avatar/ziggit.dev/integratedquantum/32/782_2.png) [@IntegratedQuantum](https://ziggit.dev/u/IntegratedQuantum)\
**Post date:** [January 10, 2026, 3:50pm UTC](https://ziggit.dev/t/typing-1024-u8-undefined-less/13887/11 "2026-01-10T15:50:36Z")

</div>

The goal of “typing xxx less” is to some degree going against a fundamental language design choice:

> “Favor reading code over writing code.”

Trying to save a few characters just to avoid having to write a type definition, or the `undefined` keyword is only going to hurt you in the end if you or someone else wants to read it again in the future.

Now the only real concern that I see here is “typing 1024 without typos”, if I understand correctly you want to enforce a fixed buffer size of 1024 bytes in your project. In that case I think the correct solution is to write more, not less, by making this an actual constant, this would also make it easy to change this value if you decide to change the size:

```zig
const stackBufferSize = 1024; // or a more specific name e.g. mentioning the protocol it comes from

...
    var buf: [stackBufferSize]u8 = undefined;

```

Beyond that I think it’s also worth questioning, why you have so many stack buffers to begin with. This may be a sign that you are not using allocators enough. Note that in Zig the right kind of allocator can be just as fast as a stack buffer.

---

<div class="post-metadata">

**Author:** ![pierrelgol](https://ziggit.dev/user_avatar/ziggit.dev/pierrelgol/32/10233_2.png) [@pierrelgol](https://ziggit.dev/u/pierrelgol)\
**Post date:** [January 12, 2026, 8:58pm UTC](https://ziggit.dev/t/typing-1024-u8-undefined-less/13887/12 "2026-01-12T20:58:38Z")

</div>

I personally find it more confusing to read :

```zig
var buffer = buf(1024);

```

Then

```zig
var stdout_buffer : [1024]u8 = undefined;

```

but to be fair I do always use type hint in my editor so I would probably see the underlying type, but the good thing with zig very explicit and simple nature, is that after using and reading it for a while, you kind of get used to it looking a certain way, and to me the latter feels already really natural to use since the new reader/writer.

But to each their own 🙂

---

<div class="post-metadata">

**Author:** ![jdevries3133](https://ziggit.dev/user_avatar/ziggit.dev/jdevries3133/32/6778_2.png) [@jdevries3133](https://ziggit.dev/u/jdevries3133)\
**Post date:** [January 13, 2026, 12:05am UTC](https://ziggit.dev/t/typing-1024-u8-undefined-less/13887/13 "2026-01-13T00:05:09Z")

</div>

> [@IntegratedQuantum](#):
>
> This may be a sign that you are not using allocators enough. Note that in Zig the right kind of allocator can be just as fast as a stack buffer.

I think you’re right on here. I think I need to shift to just taking and using alloc because a perf-sensitive caller could pass a fixed buffer allocator over a stack buffer anyway.

That being said it kind of feels bad for low-level code which knows about the memory sizes it needs to ask the caller for this black box instead of managing memory on the stack when it could. How do you strike a balance there?

---

<div class="post-metadata">

**Author:** ![pierrelgol](https://ziggit.dev/user_avatar/ziggit.dev/pierrelgol/32/10233_2.png) [@pierrelgol](https://ziggit.dev/u/pierrelgol)\
**Post date:** [January 13, 2026, 12:38am UTC](https://ziggit.dev/t/typing-1024-u8-undefined-less/13887/14 "2026-01-13T00:38:09Z")

</div>

Like Jack Reacher says “Assumption kills” what can be managed on your stack maybe cannot be managed on someone else’s stack if you care about re usability of your code (which you don’t have too just to be clear) then it’s probably for the best to not make assumptions about what should be done, I think it’s the right move to accept a slice or an allocator, again you are free to do as you wish nothings wrong here, but someone working in embedded might not have the stack space to do what you do for example.

---

<div class="post-metadata">

**Author:** ![chung-leong](https://ziggit.dev/user_avatar/ziggit.dev/chung-leong/32/999_2.png) [@chung-leong](https://ziggit.dev/u/chung-leong)\
**Post date:** [January 13, 2026, 9:18am UTC](https://ziggit.dev/t/typing-1024-u8-undefined-less/13887/15 "2026-01-13T09:18:30Z")

</div>

If functions declared as inline are semantically no different from regular functions, then what exactly is the point of the inline keyword? We might as well axe it altogether so people don’t get bitten as you put it.

---

<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:** [January 13, 2026, 9:34am UTC](https://ziggit.dev/t/typing-1024-u8-undefined-less/13887/16 "2026-01-13T09:34:58Z")

</div>

they are semantically inlined, but they are also still semantically a function call, hence the parameters get evaluated, and it’s technically not allowed to return stack pointers from them even if it works in practice.

Both of those things are things I see people repeatedly get hurt by when they try to use inline functions as if they are macros, which they are not.

---

<div class="post-metadata">

**Author:** ![chung-leong](https://ziggit.dev/user_avatar/ziggit.dev/chung-leong/32/999_2.png) [@chung-leong](https://ziggit.dev/u/chung-leong)\
**Post date:** [January 13, 2026, 2:24pm UTC](https://ziggit.dev/t/typing-1024-u8-undefined-less/13887/17 "2026-01-13T14:24:36Z")

</div>

If people are repeatedly getting hurt by a feature, then the logical response is to either modify it so that it conforms to people’s expectation or remove it.

---

<div class="post-metadata">

**Author:** ![npc1054657282](https://ziggit.dev/user_avatar/ziggit.dev/npc1054657282/32/5790_2.png) [@npc1054657282](https://ziggit.dev/u/npc1054657282)\
**Post date:** [January 13, 2026, 2:35pm UTC](https://ziggit.dev/t/typing-1024-u8-undefined-less/13887/18 "2026-01-13T14:35:29Z")

</div>

It’s hard for me to say that this is the expected approach. From my experience with other languages, I rarely expect an inline function to do things like returning a stack pointer, because I don’t expect an inline function to always be successfully inlined.  
Overall, the second point you mentioned, ‘being prohibited from doing this,’ is basically achieved for now. On `master`, attempting to do this can currently be detected as a compilation error.

---

<div class="post-metadata">

**Author:** ![pachde](https://ziggit.dev/user_avatar/ziggit.dev/pachde/32/2485_2.png) [@pachde](https://ziggit.dev/u/pachde)\
**Post date:** [January 13, 2026, 5:45pm UTC](https://ziggit.dev/t/typing-1024-u8-undefined-less/13887/19 "2026-01-13T17:45:16Z")

</div>

If a library knows the buffer sizes it needs, it can make them known to callers, similar to max path, etc, so that callers can provide the required buffer. That code can assert the size conditions it requires.

---

<div class="post-metadata">

**Author:** ![pierrelgol](https://ziggit.dev/user_avatar/ziggit.dev/pierrelgol/32/10233_2.png) [@pierrelgol](https://ziggit.dev/u/pierrelgol)\
**Post date:** [January 13, 2026, 6:18pm UTC](https://ziggit.dev/t/typing-1024-u8-undefined-less/13887/20 "2026-01-13T18:18:05Z")

</div>

I think the problem is with other languages not Zig, if I type inline in front of my function, there’s a reason, last thing I want is for LLVM to decide what it will inline or not. I don’t have an opinion on it being used as a macro, but I do expect that every function marked as inline will in fact be inlined.

[Next page](https://ziggit.dev/t/typing-1024-u8-undefined-less/13887.md?page=2)
