vulpesx
November 1, 2025, 8:27am
21
I just mis interpreted your code snippet.
1 Like
Maybe all the catch return are returning early. You could try catch @panic() instead to see if it errs
1 Like
miagi
November 1, 2025, 11:01am
23
Does not panic. Just writes a segmented output in a file
floooh
November 1, 2025, 11:27am
24
FWIW I would really like to see the function syntax changed to be more like arrow-functions or lambdas (but without capturing):
const myFunc = fn(a: i32, b: i32) i32 {
return a + b;
};
…which would then allow things like this:
const myFunc = switch(platform) {
.macOS => fn(a: i32, b: i32) i32 {
// do macOS things
},
.windows => fn(a: i32, b: i32) i32 {
// do Windows things
},
};
…would also match the rest of the syntax nicely (like const Bla = struct { ... }), but most importantly it would solve the problem to define local functions (functions inside functions) in a cleaner way (currently that’s possible but needs a wrapping namespace-struct).
4 Likes
Relevant issue:
opened 09:30AM - 13 Nov 18 UTC
closed 06:28PM - 09 Jul 23 UTC
proposal
# Overview
This is a proposal based on [#1048][issue1048] (thank you to every… one discussing in that thread). I opened this because I believe that conversation contains important ideas but addresses too many features at once.
## Goals
- Provide syntactic consistency among all statements which bind something to an identifier
- Provide syntactic foundation for a few features: functions-in-functions ([#229][issue229]), passing anonymous funtions as arguments ([#1048][issue1048])
## Non-goals
- Closures
## Motivation
Almost all statements which assign a type or value to an identifier use the same syntax. Taken from today's grammar (omitting a few decorations like `align` for brevity):
VariableDeclaration = ("var" | "const") Symbol option(":" TypeExpr) "=" Expression
The only construct which breaks this format is a function definition. It could be argued that a normal function definition consists of:
1. an address where the function instructions begin;
2. the type information (signature, calling convention) of the function;
3. a symbol binding the above to a constant or variable.
Ideally, number 3 could be decoupled from the other two.
## Proposal
Make the following true:
1. A function definition is an expression
2. All functions are anonymous
3. Binding a function to a name is accomplished with assignment syntax
```zig
const f = fn(a: i32) bool {
return (a < 4);
};
```
Roughly speaking, assigning a function to a `const` would equate to existing behavior, while assigning to a `var` would equate to assigning a function pointer.
## Benefits
- Consistency. There is alignment with the fact that aggregate types are also anonymous.
- Syntactically, this paves the way for passing anonymous functions as arguments to other functions.
- I have a suspision that this will make things simpler for the parser, but I'd love to have that confirmed/debunked by someone who actually knows (hint: not me).
- Slightly shrinks the grammar surface area:
```diff
- TopLevelDecl = option("pub") (FnDef | ExternDecl | GlobalVarDecl | UseDecl)
+ TopLevelDecl = option("pub") (ExternDecl | GlobalVarDecl | UseDecl)
```
# Examples
The `main` function follows the same rule.
```zig
pub const main = fn() void {
@import("std").debug.warn("hello\n");
};
```
The `extern` qualifier still goes before `fn` because it qualifies the function definition, but `pub` still goes before the identifier because it qualifies the visibility of the top level declaration.
```zig
const puts = extern fn([*]const u8) void;
pub const main = fn() void {
puts(c"I'm a grapefruit");
};
```
Functions as the resulting expressions of branching constructs. As with other instances of peer type resolution, each result expression would need to implicitly castable to the same type.
```zig
var f = if (condition) fn(x: i32) bool {
return (x < 4);
} else fn(x: i32) bool {
return (x == 54);
};
// Type of `g` resolves to `?fn() !void`
var g = switch (condition) {
12...24 => fn() !void {},
54 => fn() !void { return error.Unlucky; },
else => null,
};
```
Defining methods of a struct. Now there is more visual consistency in a struct definition: comma-separated lines show the struct members, while semicolon-terminated statements define the types, values, and methods "namespaced" to the struct.
```zig
pub const Allocator = struct.{
allocFn: fn(self: *Allocator, byte_count: usize, alignment: u29) Error![]u8,
reallocFn: fn(self: *Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8,
freeFn: fn(self: *Allocator, old_mem: []u8) void,
pub const Error = error.{OutOfMemory};
pub const alloc = fn(self: *Allocator, comptime T: type, n: usize) ![]T {
return self.alignedAlloc(T, @alignOf(T), n);
};
// ...
};
```
---
_Advanced mode, and possibly out of scope._
Calling an anonymous function directly.
```zig
defer fn() void {
std.debug.warn(
\\Keep it down, I'm disguised as Go.
\\I wonder if anonymous functions would provide
\\benefits to asynchronous programming?
);
}();
```
Passing an anonymous function as an argument.
```zig
const SortFn = fn(a: var, b: var) bool; // Name the type for legibility
pub const sort = fn(comptime T: type, arr: []T, f: SortFn) {
// ...
};
pub const main = fn() void {
var letters = []u8.{'g', 'e', 'r', 'm', 'a', 'n', 'i', 'u', 'm'};
sort(u8, letters, fn(a: u8, b: u8) bool {
return a < b;
});
};
```
What it would look like to define a function in a function.
```zig
pub const main = fn() void {
const incr = fn(x: i32) i32 {
return x + 1;
};
warn("woah {}\n", incr(4));
};
```
# Questions
## Extern?
The use of `extern` above doesn't seem _quite_ right, because the `FnProto` evaluates to a type:
```zig
extern puts = fn([*]const u8) void;
--------------------
this is a type
```
Maybe it's ok in the context of `extern` declaration, though. Or maybe it should look like something else instead:
```zig
extern puts: fn([*]const u8) void = undefined;
```
## Where does the anonymous function's code get put?
I think this is more or less the same issue being discussed in [#229][issue229].
# Counterarguments
- Instructions and data are fundamentally separated as far as both the programmer and the CPU are concerned. Because of this conceptual separation, a unique syntax for function body declaration is justifiable.
- Status quo is perfectly usable and looks familiar to those who use C.
[issue229]: https://github.com/ziglang/zig/issues/229
[issue1048]: https://github.com/ziglang/zig/issues/1048
And Andrew’s reasoning for rejecting the proposal
2 Likes
floooh:
…which would then allow things like this:
const myFunc = switch(platform) {
.macOS => fn(a: i32, b: i32) i32 {
// do macOS things
},
.windows => fn(a: i32, b: i32) i32 {
// do Windows things
},
};
const platform: enum { macOS, windows } = .macOS;
const myFunc = @field(struct {
fn macOS(a: i32, b: i32) i32 {
return a + b;
}
fn windows(a: i32, b: i32) i32 {
return a - b;
}
}, @tagName(platform));
4 Likes
nurpax
November 1, 2025, 6:47pm
27
I guess you weren’t 100% serious with this, but I’d still like to call out that @floooh ’s versions is a lot more readable. (And it’s also a lot more readable than the “inner struct” version which is quite commonly used.)
I think that as soon as you have multiple methods that depend on the platform, you actually want the namespace, not just the function, as it’s a nice way to bundle functions that work for a specific platform together. Like this
const WindowsFunctions = struct {
fn fun1 …
fn fun2 …
};
const MacOSFunctions = struct { … };
const PlatformSpecificFunctions = switch (platform) {
.windows => WindowsFunctions,
.macOS => MacOSFunctions,
};
and then just bite the bullet and type out PlatformSpecificFunctions.whatever any time you need to use one. Could serve as a nice reminder when you’re calling into something platform specific.
4 Likes
You can do something like this:
const std = @import("std");
fn foo(comptime i: usize) type {
return struct {
pub const result = .{
.number = i,
.something = "Hello",
.something_else = "World",
};
};
}
pub fn main() !void {
std.debug.print("{}\n", .{foo(345).result});
}
EDIT: removed unnecessary function.
1 Like
miagi
November 3, 2025, 12:58pm
30
Thank you. Yes, I was searching for something like this.