January 9, 2026, 5:15pm 1
just a case
fn do() void {
const str = comptime str: {
errdefer unreachable;
var buf: [256]u8 = undefined;
const result = try std.fmt.bufPrint(&buf, "{s}", .{"xxx"}); // edit memory region
break :str result;
};
std.debug.print("{s}\n", .{str});
}
but compile failed
error: runtime value contains reference to comptime var
std.debug.print("{s}\n", .{str});
UPDATE: This is just a simple example to get the code running—don’t worry about how to print strings at compile time.
I’ve found a way that works. Is this the right thing to do?
fn do() void {
const str = comptime str: {
errdefer unreachable;
var buf: [256]u8 = undefined;
const buf_str = try std.fmt.bufPrint(&buf, "{s}", .{"xxx"});
var result: [buf_str.len]u8 = undefined;
std.mem....
January 9, 2026, 5:15pm 1
just a case
fn do() void {
const str = comptime str: {
errdefer unreachable;
var buf: [256]u8 = undefined;
const result = try std.fmt.bufPrint(&buf, "{s}", .{"xxx"}); // edit memory region
break :str result;
};
std.debug.print("{s}\n", .{str});
}
but compile failed
error: runtime value contains reference to comptime var
std.debug.print("{s}\n", .{str});
UPDATE: This is just a simple example to get the code running—don’t worry about how to print strings at compile time.
I’ve found a way that works. Is this the right thing to do?
fn do() void {
const str = comptime str: {
errdefer unreachable;
var buf: [256]u8 = undefined;
const buf_str = try std.fmt.bufPrint(&buf, "{s}", .{"xxx"});
var result: [buf_str.len]u8 = undefined;
std.mem.copyForwards(u8, &result, buf_str);
break :str result;
};
std.debug.print("{s}\n", .{str});
}
D-Berg January 9, 2026, 5:34pm 3
You can just make it a global variable.
const str = ”xxx”;
fn do() void {
std.debug.print("{s}\n", .{str});
}
If your use-case is simply a comptime formatted string, std.fmt. has a comptime function for this.
const std = @import("std");
const str = std.fmt.comptimePrint("{s}", .{"xxx"});
pub fn main() !void {
std.debug.print("{s}\n", .{str});
}
Your code is failing because buf only has a scope for the block it is within.
1 Like
I know about std.fmt.comptimePrint; it’s just an example of manipulating memory.
Yes this is the right approach.
FYI, you should use @memcpy here instead of copyForwards.
You asked what the right thing to do was. comptimePrint is the correct way to express this code.
But for things like this, (that can’t be expressed as a print statement) I’ve taken to putting all the code in a function, and const name = comptime realFunction(); I find it’s easier to reason about over thing = comptime brk: { [ some inline block ] };
Other than that, I’d probably read the zig docs about both arrays as a whole, and static local variables. Because most everything (that’s not runtime allocated) are all “embedded memory” within the binary. And, importantly as the docs say:
Zig programmers must always be able to answer the question: Where are the bytes?
So knowing how to get the kind you want, and where they live is an important aspect of zig. 
1 Like
Thank you for your suggestion. I’m perfectly clear about where the “bytes” are. The point of my post isn’t about embedding binary data, but rather how to solidify a portion of data in compile-time memory.
Are you sure? Because in your title post, var buf, which is where you printed the string, goes “out of scope” when you reach the break :str so const result is pointing to a memory location that you don’t control anymore. Knowing where the bytes are means you have to know when you have a slice vs when you have an array. comptimePrint is the correct way to write this, because it doesn’t return a pointer to the memory. It returns an array of bytes, similar to how you found copying the data to var result: [buf_str.len]u8 works. Because you’re creating an array of a defined size. And then copying the printed string into it, (and then possibly copying those bytes a 3rd time when result is moved to str. (But to be fair, the compiler is likely to figure that out, and omit one or both copies).
But The way to create a const block of memory, based on comptime logic, is inline with your second code block. Calculate the array size needed, write the data into the var array at comptime, and then copy that data into a const array of the same size. The code in comptimePrint is a slightly better version of your 2nd example.
That’s because I naïvely assumed the compiler could infer my intention of needing the result data and would therefore automatically create a piece of data to serve as the value of str, while letting buf simply disappear.
Yeah, I know what you mean. It’s both a blessing and a curse that zig refuses to try to guess what you want. On the plus side, you don’t have to wonder if you’re gonna get all 256 bytes stored “somewhere” in your program, for the comptime created slice, or even just 3, because the compiler figured out you only use 3. But on the down side, you have to specify explicitly what you want, and exactly where you want it to live. But then, I don’t really consider that a down side.