P H Kaznowski
P H Kaznowski

Reputation: 301

Why are const pointers different to var pointers in Zig

I have the following in Zig

 const std = @import("std");

 var messages = [2]*[]u8{undefined, undefined};

 pub fn main() !void {
     // Allocate some strings
     const msg1 = "First message";
     const msg2 = "Second message has different length";
     const allocator = std.heap.page_allocator;
     const msg1_ptr = try allocator.alloc(u8, msg1.len);
     defer allocator.free(msg1_ptr);
     const msg2_ptr = try allocator.alloc(u8, msg2.len);
     defer allocator.free(msg2_ptr);
     messages[0] = @as(*[]u8, &msg1_ptr);
     messages[1] = @as(*[]u8, &msg2_ptr);

It gives the following error

src/main.zig:14:30: error: expected type '*[]u8', found '*const []u8'
    messages[0] = @as(*[]u8, &msg1_ptr);
                             ^~~~~~~~~
src/main.zig:14:30: note: cast discards const qualifier
referenced by:
    callMain: /usr/local/Cellar/zig/0.13.0/lib/zig/std/start.zig:524:32
    callMainWithArgs: /usr/local/Cellar/zig/0.13.0/lib/zig/std/start.zig:482:12
    remaining reference traces hidden; use '-freference-trace' to see all reference traces

To fix it, it was enough to change the const to var for the pointers

     var msg1_ptr = try allocator.alloc(u8, msg1.len);
     defer allocator.free(msg1_ptr);
     var msg2_ptr = try allocator.alloc(u8, msg2.len);
     defer allocator.free(msg2_ptr);

Why does a const pointer not cast to a const-agnostic pointer, but changing it to var is valid? Is it because const implies immutable, but var does not so has less restrictions? Does that mean const pointers can never be cast?

Upvotes: 0

Views: 483

Answers (1)

sigod
sigod

Reputation: 6486

Is it because const implies immutable, but var does not so has less restrictions?

The const on a local variable means that after the initialization the variable's value will not change. Technically, you could cast away the const and try to change the variable, but its bytes might be in read-only memory.

In this case, msg1_ptr is a slice ([]u8), which is a fat pointer - it contains the pointer to the data and the length. The const implies that the slice itself cannot change, but the data is not declared as const (it is u8, not const u8), so it can change.

By taking an address of msg1_ptr, you get a const pointer. A const pointer implies that the memory will be read, but not changed / written to. For example, functions that want to read something by a pointer, but not change it, will typically declare the argument as const. Take std.mem.indexOf as an example:

fn indexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize

Why does a const pointer not cast to a const-agnostic pointer, but changing it to var is valid?

@as is for type coercion. It does not cast types.

Switching from const to var is the correct solution here. If you don't want it to be const, don't declare it as const in the first place.

Does that mean const pointers can never be cast?

The @constCast can be used to remove the const qualifier from a pointer. But you should only use it in exceptional cases.

Upvotes: 1

Related Questions