Assigning, copying, and converting arrays

I’m having some issues with zig arrays. I thought they were supposed to be similar to a scalar type in handling and not degrade to pointers like C (ie, when I call a function with an array argument, the whole array is passed by value, and it does’t turn into a pointer). Iit sometimes acts as a scalar – when you take the address you get a single item pointer – but sometimes act as a multivalues type since it is indexable. It’s a Shrodinger type - depending on how you interact with it you get different properties.

But I can’t seem to convert an array literal into a slice. How do I get array a into const and a non-const slices?

(1) Why is this not allowed? const before the a say’s I won’t reassign it. I understand this to some extent since arrays are supposed to be like screadonlyalars, but the type of the items is not u32 but is const u32. They are stored in the rodata section binary, and no matter what I do, they will always be immutable. How do I carry that information around?

(2) This b isn’t the same type as a. Binding a doesn’t contain mutable data, But b seems to say it does. And b is never modified, so is var/const different for arrays? Depending on what comes after this I need to ikeep changing this from var to const even though none of the code ever mutates the values, just the compiler can’t seem to prove it to itself.

(3) For a scalar this would make a binary copy, and I believe it would do the same for a @Vector, but this doesn’t seem to work for arrays.

(4) I thought b[0..] would return a slice of all of b. There does this pointer come from?

    // (1) const a: [3]const u32 = [_]u32{ 3, 1, 4 }; // pointer modifier const not allowed on array child type
    const a = [_]u32{ 3, 1, 4 };
    // (2) var b: [3]u32 = a;
    const b: [3]u32 = a;     // (3) 
    const t: []u32 = b[0..]; // (4) expected []u32, found *const [3]u32

I can’t figure how to slices (mutable and immutable) of an array literal. It keeps winding up as a pointer to an array.

Side note: Is there a way to cast a [3]u32 to a [2]u32 easily like how @intCast cn be used to cast from a u64 to a u32?

You can get const slices without any restriction:

const a = [_]u32{ 3, 1, 4 };
const slice1: []const u32 = a[0..a.len];
const slice2: []const u32 = &a;

const string: *const [5:0]u8 = "hello";
const str: []const u8 = string;

To get non-const slices, the array must be mutable (a var):

var a = [_]u32{ 3, 1, 4 };
const slice1: []u32 = a[0..a.len];
const slice2: []u32 = &a;

so an array is not literal in rodata like a double quoted string? An anonymous struct literal is also rodata? The rest seem odifyable

var a = .{ @as(u32, 1), "asdf") };
a[0] = 3;

that errors but if I make it a tuple or regular struct it seem those are modifyable.

Still where does that pointer come from in (4)?

When the slice has comptime-known bounds, the compiler will give you a pointer to an array, since there’s no need for a runtime len field. They can implicitly cast to slices. In (4), you’re trying to create a alice to mutable data, but the array is const.
In your last example, "asdf" lives in the const data section, but since you created a var, Zig will implicitly copy that into a variable on the stack, so that it is mutable.

1 Like

But I can’t modifty it. In the asdf example, that errors even though it is var. What am I missing?

Ops, sorry. The type of a string literal is *const [_:0] u8 because its pointing to data that lives in a const section, hence why you can’t modify it. If you want a var with the contents of the string literal, you have to make it yourself, zig won’t implicitly copy it:

const literal = "asdf"; //type deduced to * const [4:0]u8
var mutable: [4]u8 = undefined;
@memcpy(&mutable, literal);

Look a little closer. The anonymous struct has 2 fields. The string is the second field. I’m trying to modify the first field that is just a u32, but it errors. If I make that a named struct all is good.

There’s something going on I’m not understanding because it seems some types are implicitly copied and some arent. Like anonymous vs name strructs seem to behave differently when created by assigning from a literal.

Don’t aske me why this works (I would love to know too):

const std = @import("std");

pub fn main() !void {
    const Tuple = std.meta.Tuple(&.{ u32, []const u8 });
    var a: Tuple = .{ 123, "asdf" };
    std.debug.print("{} {s}\n", .{ a[0], a[1] });

    a[0] = 456;
    a[1] = "gotcha!";
    std.debug.print("{} {s}\n", .{ a[0], a[1] });
❯ zig build run
123 asdf
456 gotcha

This is easier:

const std = @import("std");

pub fn main() !void {
    var a: struct { u32, []const u8 } = .{ 123, "asdf" };
    std.debug.print("{} {s}\n", .{ a[0], a[1] });

    a[0] = 456;
    a[1] = "gotcha!";
    std.debug.print("{} {s}\n", .{ a[0], a[1] });

When running:

const std = @import("std");

pub fn main() !void {
    const a = .{ @as(u32, 1), "asdf" };
    std.debug.print("{}\n", .{@TypeOf(a)});

The output is: struct{comptime u32 = 1, comptime *const [4:0]u8 = "asdf"}