Zig DISCUSSION

Why can't I modify a function parameter in Zig, and what is the right way to mutate it?

Started by cedric Zig function parametersimmutable parameterspass by pointerZig structsmutable copy
5 replies 248 views 6 participants
Latest activity · 30 Sep 2026

Why can't I modify a function parameter in Zig, and what is the right way to mutate it?

cedric Zig Forum
#1

I am porting a small C helper to Zig. In C I wrote int scale(int v) { v *= 2; return v; } and changed the parameter freely. The Zig version fn scale(value: u32) u32 { value *= 2; return value; } does not compile: the compiler says it cannot assign to a constant.

Why are parameters constant in Zig, and what is the idiomatic way to either work on a copy or actually change the caller's variable? I also have a configuration struct of about 4 kB that I want a function to update in place.

Community replies 5

Re: Why can't I modify a function parameter in Zig, and what is the right way to mutate it?

#2

All function parameters in Zig are immutable, and there is no way to declare one as var. If you only need a scratch copy, make one explicitly: var v = value; v *= 2; return v;. The copy must have a different name, because Zig does not allow a local to shadow a parameter.

For a u32 this costs nothing; the optimiser keeps the value in a register either way.

Re: Why can't I modify a function parameter in Zig, and what is the right way to mutate it?

#3

The rule follows from how arguments are passed. Integers, floats and pointers are simply copied. For structs, unions and arrays the compiler may pass either a copy or a hidden reference, whichever it judges cheaper, and that freedom is only safe if the callee cannot write to the parameter. Your 4 kB struct taken as cfg: Config does not have to be copied as 4096 bytes on every call, even though you wrote no pointer.

One consequence: do not treat a by-value struct parameter as a guaranteed snapshot. If the function also modifies the original through a pointer or a global while it runs, the parameter may or may not see the change.

Re: Why can't I modify a function parameter in Zig, and what is the right way to mutate it?

#4

To change the caller's variable, take a pointer and say so at the call site: fn scaleInPlace(value: *u32) void { value.* *= 2; }, called as scaleInPlace(&n) where n is declared with var. value.* is the dereference. For the struct: fn setBaud(cfg: *Config, baud: u32) void { cfg.baud = baud; }. Field access through a pointer needs no special operator; cfg.baud works on both a struct and a pointer to one.

If the function only reads a large struct and you want to be explicit about not copying, use *const Config.

Re: Why can't I modify a function parameter in Zig, and what is the right way to mutate it?

#5

The same distinction shows up in methods. A method that modifies its object declares self: *Config, one that only reads declares self: Config or self: *const Config. Calling cfg.reset() passes the address automatically when the method wants a pointer. If cfg itself was declared with const, that call is rejected because a *Config is expected and only a *const Config is available, so the fix is at the declaration: var cfg = Config{};.

Re: Why can't I modify a function parameter in Zig, and what is the right way to mutate it?

#6

Arrays and buffers follow from the same rules. A slice parameter buf: []u8 is itself constant, so you cannot make buf point elsewhere, but the bytes it refers to are writable: buf[0] = 0xFF; changes the caller's data. Declare it []const u8 when the function must not write.

To pass an array that way, take its address: with var data = [_]u8{ 1, 2, 3 }; the call fill(&data) hands over a slice of all three elements, whereas passing data itself to a [3]u8 parameter gives the function a read-only value.

TEP COMMUNITY