6

I'm a total noob to learning Zig so my question may be naïve.

I'm trying to create a variety of strings in my code using the string formatting functions in the standard library such as std.fmt.allocPrint()

It was working when I was just doing one string at a time but when I decided to abstract it into a function the compiler is telling me that allocPrint's format string is comptime.

I can't find any way to achieve the same thing using a runtime format string.

Is there a totally different way that involves not using std.fmt or am I missing something basic due to being a beginner?

10
  • 1
    no. not yet.... Commented Jun 12, 2022 at 6:29
  • 1
    Is it possible for you to change your function to accept a comptime format string too? eg fn myFn(format: []const u8)fn myFn(comptime format: []const u8). If not, could you give a code example of what you're trying to do and why it has to be runtime? Commented Jun 13, 2022 at 20:47
  • 1
    @hippietrail It shouldn't be possible to convert a runtime string to a comptime string, as the value of a runtime string can't be known at comptime. If the code compiled, that is likely a bug. The discord is probably a good place to ask about something like that: github.com/ziglang/zig/wiki/Community Commented Jun 14, 2022 at 16:38
  • 1
    @hippietrail To iterate over comptime strings, you need to use a comptime loop: inline for(.{"string one {s}", "string two {s}"}) |format_str| { std.log.info(format_str, .{"arg"}); } ziglang.org/documentation/master/#inline-for . You can't make a function that returns a comptime string from a runtime string though, because the result of that function needs to be known at comptime but depends on a runtime value Commented Jun 14, 2022 at 21:02
  • 1
    @pfg Thanks. So you can't inline such a function like you can inline a loop? I thought either way it would end up unrolling/instantiating since the format strings are always part of a fixed set known at compile time. Commented Jun 15, 2022 at 4:49

1 Answer 1

2

Zig's printing functions expect format strings that are known at compile time. You can use C's printf if you need a dynamic format string.

Here's something I got running on version 0.12.0-dev.2296+46d592e48.

const c = @cImport({
    @cInclude("stdio.h");
});

const std = @import("std");

// Returns a null-terminated heap-allocated string, usable in C, containing the phrase "hi " repeated numHis type.
const hi = "hi ";
fn getHis(allocator: std.mem.Allocator, numHis: usize) ![*:0]u8 {
    const total_len = numHis * hi.len;
    // allocSentinel ensures it ends with a \0.
    const s = try allocator.allocSentinel(u8, total_len, 0);
    var i: usize = 0;
    while (i < total_len) : (i += hi.len) {
        // repeatedly copy the phrase into the allocated memory.
        std.mem.copyForwards(u8, s[i .. i + hi.len], hi);
    }
    return s;
}

// Returns a null-terminated heap-allocated string, usable in C, containing some border chars and the phrase "%s", which can be passed as a format string to c's printf.
const strInsert = "\n%s\n";
fn getBorderedFormatString(allocator: std.mem.Allocator, borderChar: u8) ![*:0]u8 {
    const total_len: usize = 20 + strInsert.len + 2; // add 2 for leading and trailing newline
    const s = try allocator.allocSentinel(u8, total_len, 0);
    var i: usize = 0;
    s[i] = '\n';
    i += 1;
    while (i < 11) : (i += 1) {
        s[i] = borderChar;
    }
    std.mem.copyForwards(u8, s[i .. i + strInsert.len], strInsert);
    i += strInsert.len;
    while (i < total_len - 1) : (i += 1) {
        s[i] = borderChar;
    }
    s[i] = '\n';
    return s;
}

test "dynamic_phrase" {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    const allocator = arena.allocator();
    const s = try getHis(allocator, 30);
    const f = try getBorderedFormatString(allocator, '+');
    _ = c.printf(f, s);
}

I'm new at Zig, so maybe there is a way to use dynamic format strings in Zig directly. If so, I didn't easily discover it.

Sign up to request clarification or add additional context in comments.

2 Comments

It never occurred to me to use the C interop! I ended up writing my own partial implementation of allocPrint without runtime format strings and I kept trying to get something working where the format strings were comptime but the logic of selecting which one was runtime. I can't remember if I got that working in the end as it was just a learning exercise and I moved on.
I did this as a learning exercise too. I think I probably don't actually need dynamic format strings, but I got a bee in my bonnet about not being able to obviously do it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.