Doesn't `const` imply immutability?

Hi All - New to Zig, and there are many things I can appreciate about it. I am looking at the following simple code, and trying to wrap my head around, how we are writing to a const conn. Isn’t writing to conn.stream mutating it? Doesn’t const imply immutability?

const std = @import("std");

pub fn main() !void {

    const address = try std.net.Address.resolveIp("127.0.0.1", 1776);
    var listener: std.net.Server = try address.listen(.{
        .reuse_address = true,
    });
    defer listener.deinit();

    const conn = try listener.accept();
    defer conn.stream.close();

    try conn.stream.writeAll("HTTP/1.1 200 OK\r\n\r\n");
}

This might have been discussed before (and I looked at net.Stream.writeAll implementation), but I could not find a good explanation for this. Thanks in advance for your help.

Hey, welcome to Ziggit!

If you look at the implementation of std.net.Stream.writeAll() you will see that the parameter self: Stream is passed by value (copied), so it doesn’t actually care about whether the original stream is mutable or not.

This works because Stream is just a wrapper around a socket_t which is a handle of a resource controlled by the OS (similiar to std.fs.File wrapping a fd_t) so the mutable state is not actually ‘part’ of your program:

4 Likes

@bamse
Also I’m new to Zig, but I guess the constant declaration refers to the object pointed to by the variable and not its contents.

This would be the case if std.net.Server.accept() returned a !*Connection, then the const would only affect the pointer and not to the Connection it points to.
But it returns a !Connection and const propagates recursively through containers so conn and all its fields (including conn.stream) are const here.

3 Likes

@Justus2308

Thank you, I see that there are many peculiarities to be explored in this language. I must say that up to now I agree almost entirely with the choices made in its project, and this rarely happens, but undoubtedly many mechanisms I still have to study in depth.

1 Like

Zig is like C and unlike JS:

  • const values are neither re-assignable nor can their content be mutated
  • for pointers there are four scenarios (like C, but the Zig syntax makes a lot more sense):
    • a const-pointer (not re-assignable) to const-data (immutable)
    • a const-pointer (not re-assignable) to var-data (mutable)
    • a var-pointer (re-assignable) to const-data (immutable)
    • a var-pointer (re-assignable) to var-data (mutable)

What is a bit confusing in Zig (when coming from C or JS) is that function args are implictly const.

3 Likes

Thank you, all, for your helpful comments. In addition to the items mentioned above, maybe something that I overlooked in the relationship between conn and listener is that conn.stream is based on listener.stream (which is initialized and owned by listener), and listener is declared as var. Hence the socket_t object that conn.stream.handle is pointing to is mutable.