Skip to content

What Anonymous Functions Usually Mean

An anonymous function is a function without a permanent name.

Anonymous Functions

An anonymous function is a function without a permanent name.

Many programming languages support anonymous functions heavily. JavaScript, Python, Rust, and Go all provide different forms of them.

Zig takes a more conservative approach.

Zig does not currently have full closure-based anonymous functions like JavaScript or Python. Instead, Zig prefers:

  • named functions
  • compile-time function generation
  • structs with methods
  • explicit state passing

This design keeps behavior predictable and avoids hidden allocations and hidden captures.

Even though Zig’s approach is different, understanding anonymous-function concepts is still important because you will encounter them in other languages and in Zig design discussions.

What Anonymous Functions Usually Mean

In many languages:

function value without a declared name

JavaScript example:

const add = function(a, b) {
    return a + b;
};

Python example:

square = lambda x: x * x

These functions are created inline.

Zig’s Philosophy

Zig intentionally avoids automatic hidden behavior.

Closures in many languages often capture variables automatically:

function makeCounter() {
    let count = 0;

    return function() {
        count += 1;
        return count;
    };
}

The inner function silently captures:

count

This hidden state may require heap allocation or compiler-generated structures.

Zig prefers explicit data structures instead.

Named Functions as Values

Even without traditional anonymous functions, Zig functions are still values.

Example:

const std = @import("std");

fn add(a: i32, b: i32) i32 {
    return a + b;
}

pub fn main() void {
    const op = add;

    const result = op(2, 3);

    std.debug.print("{}\n", .{result});
}

Output:

5

This gives much of the flexibility people want from anonymous functions.

Inline Struct-Based Behavior

Instead of closures, Zig often uses structs.

Example:

const Adder = struct {
    value: i32,

    fn apply(self: Adder, x: i32) i32 {
        return x + self.value;
    }
};

Using it:

const add10 = Adder{
    .value = 10,
};

const result = add10.apply(5);

Result:

15

This behaves similarly to a closure:

captured value = 10

But everything is explicit.

Explicit State Is Important

Compare these two ideas.

Hidden capture:

function silently stores outside variables

Explicit state:

struct explicitly contains state

Zig strongly prefers the second approach.

This makes ownership and memory behavior easier to understand.

Compile-Time Function Generation

Zig often replaces anonymous-function patterns with comptime.

Example:

fn makeAdder(comptime amount: i32)
    fn(i32) i32
{

}

Conceptually:

generate specialized behavior at compile time

Zig relies heavily on compile-time programming instead of runtime closures.

You will study this deeply later.

Callback Style APIs

Many languages use anonymous functions for callbacks.

JavaScript example:

items.forEach(function(item) {
    console.log(item);
});

In Zig, this is usually written with named functions.

Example conceptually:

fn printItem(item: i32) void {
    std.debug.print("{}\n", .{item});
}

Then passed as a normal function pointer.

Why Zig Avoids Heavy Closure Systems

Closure systems can introduce:

  • hidden allocations
  • hidden memory ownership
  • hidden captures
  • unclear lifetimes
  • implicit heap usage

Zig prefers:

  • explicit memory behavior
  • visible ownership
  • predictable performance
  • simple compiler rules

This aligns with Zig’s systems-programming goals.

Function Factories with Structs

Suppose we want customizable behavior.

In JavaScript:

function makeMultiplier(n) {
    return function(x) {
        return x * n;
    };
}

In Zig:

const Multiplier = struct {
    factor: i32,

    fn apply(self: Multiplier, x: i32) i32 {
        return x * self.factor;
    }
};

Using it:

const times3 = Multiplier{
    .factor = 3,
};

const result = times3.apply(4);

Output:

12

Again, the captured state is explicit.

Temporary Inline Structs

Sometimes Zig uses anonymous structs inline.

Example:

const value = .{
    .x = 10,
    .y = 20,
};

This is anonymous data, not an anonymous function, but it follows the same philosophy:

simple explicit structures instead of hidden magic

Methods as Behavioral Units

Methods often replace closure usage.

Example:

const Counter = struct {
    value: i32,

    fn increment(self: *Counter) void {
        self.value += 1;
    }
};

The struct stores state.

The method stores behavior.

This combination replaces many closure patterns naturally.

Generic Behavior Without Closures

Zig frequently uses generic functions.

Example:

fn process(
    comptime T: type,
    value: T,
) void {

}

Compile-time polymorphism often removes the need for runtime anonymous functions.

Simulating Closures Manually

You can manually build closure-like systems.

Example:

const Callback = struct {
    context: *anyopaque,
    call: *const fn(*anyopaque) void,
};

This pattern appears in low-level libraries.

The callback stores:

  • function pointer
  • explicit context pointer

C libraries commonly use this style too.

Example Callback Context Pattern

const Context = struct {
    count: i32,
};

fn callback(ptr: *anyopaque) void {
    const ctx: *Context =
        @ptrCast(@alignCast(ptr));

    ctx.count += 1;
}

This style is verbose but extremely explicit.

Nothing is hidden.

Anonymous Functions vs Readability

Anonymous functions can sometimes reduce readability.

Example:

items.map(x => x * 2)
     .filter(x => x > 10)
     .reduce((a, b) => a + b)

Compact code is not always clearer code.

Zig generally favors explicit named steps instead.

A Complete Zig Example

const std = @import("std");

const Printer = struct {
    prefix: []const u8,

    fn print(
        self: Printer,
        text: []const u8,
    ) void {
        std.debug.print(
            "{s}: {s}\n",
            .{
                self.prefix,
                text,
            },
        );
    }
};

pub fn main() void {
    const info = Printer{
        .prefix = "INFO",
    };

    const error_log = Printer{
        .prefix = "ERROR",
    };

    info.print("starting");
    error_log.print("failed");
}

Output:

INFO: starting
ERROR: failed

This behaves similarly to closure-based customization, but the state is stored explicitly inside structs.

Mental Model

Many languages use anonymous functions like this:

behavior + hidden captured state

Zig prefers:

behavior + explicit structures

Instead of relying heavily on runtime closure systems, Zig encourages:

  • named functions
  • structs
  • methods
  • compile-time specialization
  • explicit context passing

This keeps programs easier to reason about, especially in low-level systems programming where memory behavior matters.