Hello,
Why does Zig not have immutability by default for slices/pointers?
Currently:
const s1: [] u8 = ...; → elemenst s1 points to can be mutated
const s1: [] const u8 = ...; → elements s1 points to cannot be mutated
(s1 can’t be reassigned to in both cases)
My suggestion: why not the other way around?
const s1: [] u8 = ...; → elemenst s1 points to cannot mutated
const s1: [] var u8 = ...; → elements s1 points to can be mutated
It feels safer to me: I often forget to make a pointer / slice immutable (therefore opening the door for accidental mutation later).
Zig puts a big emphasis on safety so I was surprised when I found out it got the defaults for constness (IMO) the other way around.
Rust, for example, does it this way and I think it helps a lot.
When it comes to readibility: In my experience (feel free to share yours), the ratio of mutable to immutable pointers is more or less 50/50, tending slightly more to immutable to (e.g. string literals). So IMO this change would either keep readability the same or improve slightly by removing unnecessary consts. Also, var is the smaller keyword (though I’m being pedantic at this point)
I found this issue where this exact topic is suggested, but it was closed without much comment. @andrewrk, can you elaborate on why this isn’t the right choice for Zig? Thanks in advance
I know the language as come a long way since this sort of questions was an open problem, but it feels like it would make the language safer and more consistent. Also, intuitively it seems that this change could be applied algorithmically to existing codebases. (someone correct me if I’m wrong)
EDIT 1: Some people have conflated two separate suggestions ![]()
- Making
[] u8/* u8mean “slice/pointer to const data” - Requiring
[] var u8/* var u8(i.e. not having a default “const/var ness” to pointer types) when using a slice/pointer to mutable data.
This post was made to suggest 1), not 2). That aside, 2) has some merit in my view. They both achieve the same goal of removing what I consider to be a (slightly) unsafe default. 2) feels more consistent than 1), but I’d say I don’t like 2) as much as 1) just because it increases verbosity, whereas 1) decreases it (if you subscribe to my and some other people’s argument that const pointers are more frequent than non-const pointers)
EDIT 2: An example of a programming mistake that can be prevented by this change
Currently, if you want to have a safe zig program, then invariably you will often have to think: “should this pointer be const?”.
My suggestion: turns this around and makes you think “should this pointer be var?”
In the scenario where you fail to think, with this suggestion you get a safer outcome.
Example: a function that takes in two slices: one (a1) can be mutated, the other (a2) shouldn’t be mutated:
// poorly named variables, but sometimes you don't have control over that.
// a2 should be const, but I forgot to add the keyword :( because I was distracted e.g. by the smell of freshly baked quiche
fn f(a1: [] u8, a2: [] u8) void {
// I have now created an unsafe/less-safe zone where
// I can mix them up on accident and modify a2
}
My suggestion would change this to:
// still poorly named variables
// a2 is const by default. if you try to modify it, you get a compile error
fn f(a1: [] var u8, a2: [] u8) void {
// if you forget the `var` on a1, compiler will remind you when you try to mutate it
// you cannot accidentally mutate a2
}