On Discord last night I was trying to understand Zig const, and I think I got it, but there were a few concepts that I couldn’t find a way to say and others said aren’t possible. I’m mostly C++ where it inherits C’s Right to Left reading.It always works and the only deviation is the final const can before or after the type. Besides that it is highly regular and covers all the bases.
The big different I was told was const is an attribute of a pointer that describes that it points at. More concretely,C++ vs Zig:
C++ int const * p;
p is a pointer to constant int.
Zig var p: *const
p is a constant pointer to an int
C++ int * const p;
p is a constant pointer to a mutable int.
Zig const p: *int;
??? How do you say this is Zig’s nomenclature ???
Overall its very confusing and seems to confuse a binding with a memory address. I think that was the attempt, to make the binding take responsibility for the constness, but somehow it got confused the the memory address.
If you are coming fro C++, place all the consts where you normally would, then slide them all one and that is where they end up.
Why does Zig use a non-standard wordiing? It would seem pretty fire that the English itself cannot represent everything needed.
I’m not sure of what is the exact issue but this may be because I don’t master C++. Anyway, here is a small Zig program with comments to illustrate the two possibiities, Mutable pointer to a constant value and Constant pointer to a mutable value:
const std = @import("std");
pub fn main() void {
const i: i32 = 7;
var j: i32 = 9;
var p1: *const i32 = &i; // Mutable pointer to a constant value
const p2: *i32 = &j; // Constant pointer to a mutable value
// p1.* = 3; // "cannot assign to constant"
p2.* = 5; // Updates j
p1 = &j;
// p2 = &i; // "cannot assign to constant"
std.debug.print("{d} {d}\n", .{p1.*, p2.*});
}
Please note that a * const T does not say that the T being pointed to is immutable or constant! It is simply that this pointer does not have permission to change the it, not that nobody has permission.
He’s funny. That’s now what I was talking about. It’s a lttle more than just style decisions of left or right. Eg, if Zig made const apply to the right
let i : const * const T
or const c: * const T
leads to things being much more consistent and readable.
const c : i32 = 1;
var v : i32 = 1;
assert(@TypeOf(c) == @TypeOf(v));
Well, that becomes a matter of opinion. I think it should be true. It makes more sense semantically and, in my code, this being false would create a lot of trouble.
Its forgets the binding maybe. Type of pointer to c/v is a function c/v and the binding to c/v?) Right now type(addressof(int)) is unknown. going to go ask the language theory guys. In the execuble not all locations ar the same so that isn’t understood by Zig.
Zig doesn’t differentiate between memory that can be written to a memory that cannot be written to. Only pointers that have permission to modify a memory location and pointers that don’t. This works very well for preventing mistakes while still allowing enough flexibility to handle edge cases and I’m not sure how the c++ model (which I do not claim to understand) would provide any benefit over the current system.
If you get a pointer to constant memory, like a string literal or an embedded file, you get a const pointer that doesn’t allow modification. Technically you can perform a @constCast to get a non-const pointer, but writing to it would cause a segmentation fault. In my mind this is fine, since it requires the use of a built-in that signals to the programmer that this is unsafe behavior and needs closer inspection (and perhaps a comment explaining why it is valid in the context where it is used).