I have a scenario where I want a function to return one of several fixed slices of string literals, which have different lengths (so I can’t use an array return type), depending on the argument.
Here is a contrived example:
const std = @import("std");
const Amount = enum { one, two };
fn returnSlice(n: Amount) []const []const u8 {
return switch (n) {
.one => &.{"hello"},
.two => &.{"foo", "bar"},
};
}
pub fn main() void {
const slice = returnSlice(.two);
std.debug.print("{s}\n", .{slice});
}
If .one
is passed, it prints { hello }
, otherwise it prints { foo, bar }
. So it seems to work as expected.
However, I am unsure if this is the correct way to do it, since I am returning a slice, i.e. a form of pointer, to a value that I did not allocate on the heap.
I found one case in the Zig compiler sources which is very similar to mine, in src/targets.zig
:
pub fn libcFullLinkFlags(target: std.Target) []const []const u8 {
// The linking order of these is significant and should match the order other
// c compilers such as gcc or clang use.
return switch (target.os.tag) {
.netbsd, .openbsd => &[_][]const u8{
"-lm",
"-lpthread",
"-lc",
"-lutil",
},
// […]
This gives me some confidence, but here they use &[_][]const u8{ … }
instead of &.{ … }
. Does that make a difference?
Where are the arrays that I am referencing actually stored in memory? Did my code work on accident or is there a footgun waiting to happen (Pointers to Temporary Memory)?