Zig NEWS

loading...

Discussion on: What's a String Literal in Zig?

Collapse
david_vanderson profile image
David Vanderson

This is good - string literals are still something I'm stumbling over.

var foo: [50]u8 = undefined;
foo = "bar"; // compile error here

I think I can do this in a function that uses comptime to introspect the size of the string literal:

fn assignStr(out: []u8, str: [:0]const u8) void {
for (str) |c, i|
out[i] = c;
out[str.len] = 0;
}

assignStr(&foo, "bar"); // works

But this doesn't help in struct initializers:
const A = struct {
foo: [10]u8,
};

var a = A{.foo = "bar"}; // Is there a way to make this work?

Collapse
mrkishi profile image
mrkishi • Edited

I don't think there's a way to directly assign a smaller array to a larger one, but you can always create a helper function to do so:

fn array(comptime T: type, comptime size: usize, items: ?[]const T) [size]T {
    var output = std.mem.zeroes([size]T);
    if (items) |slice| std.mem.copy(T, &output, slice);
    return output;
}

const A = struct {
    foo: [10]u8,
};

// fully initialize
var foo: [3]u8 = .{'f', 'o', 'o'};

// zero initialize then copy desired contents
var bar: [50]u8 = [_]u8{0} ** 50;
std.mem.copy(u8, &bar, "bar");

var baz: A = A{.foo = [_]u8{0} ** 10};
std.mem.copy(u8, &baz.foo, "baz");

// fully initialize with comptime concatenation
var bar_full: [50]u8 = "bar".* ++ [_]u8{0} ** 47;
var baz_full: A = A{.foo = "baz".* ++ [_]u8{0} ** 7};

// with helper function
var bar_alt = comptime array(u8, 50, "bar");
var baz_alt = A{.foo = comptime array(u8, 10, "baz")};
Enter fullscreen mode Exit fullscreen mode
Collapse
david_vanderson profile image
David Vanderson

Thanks - great to see different options for how to do it!

Can you think of a way to use the helper function and somehow infer the size of the struct member so it doesn't have to be repeated? Not a huge deal but I can't figure out how to do it.

Thread Thread
mrkishi profile image
mrkishi

I don't know how to do that exactly. The closest I could come up with would be to leave the array undefined and use a helper function to fill it in, but that has slightly different semantics since the memcpy is a little more explicit:

fn init_array(target: anytype, items: ?[]const std.meta.Elem(@TypeOf(target))) void {
    const T = std.meta.Elem(@TypeOf(target));
    if (items) |slice| {
        std.mem.copy(T, target, slice);
        std.mem.set(T, target[slice.len..], std.mem.zeroes(T));
    } else {
        std.mem.set(T, target, std.mem.zeroes(T));
    }
}

var foo: [50]u8 = undefined;
init_array(&foo, "foo");

var a = A{.foo = undefined};
init_array(&a.foo, "foo");
Enter fullscreen mode Exit fullscreen mode
Thread Thread
david_vanderson profile image
David Vanderson

Good solution. I haven't seen std.meta.Elem before so I'm going to go read up on that. Thanks!