std.Io.Writer.fixed: How to get length of written data?

In the bellow example, what is the most correct way to obtain the length of the written data to a fixed buffer?
I am most concerned with:

  1. using the writer interface correctly (not using internal data incorrectly)
  2. using the writer interface in a manner that is least likely to change or be deprecated in the next release cycle
const std = @import("std");

// returns length of data written
fn sayHello(out: []u8) error{WriteFailed}!usize {
    var writer = std.Io.Writer.fixed(out);
    try writer.writeAll("hello");
    // which one of these return statements is (most) correct?
    return writer.end;
    return writer.buffered().len;
}

test {
    var out: [10]u8 = undefined;
    try std.testing.expectEqual(5, try sayHello(&out));
}

(I understand that sayHello would normally accept a *std.Io.Writer as a parameter, but baby steps here for me, I am slowly migrating to putting writers everywhere).

1 Like

return writer.end;

The implementation of std.Io.Writer.buffered() is

pub fn buffered(w: *const Writer) []u8 {
    return w.buffer[0..w.end];
}

so return w.end and return w.buffered().len are currently exactly equivalent, except that safe release modes might insert a redundant array bounds check when slicing.

I believe there are plans to add a seek (start of buffered not-yet-flushed data) field to Writer, turning w.buffer into a ring buffer. This should not affect the vast majority of normal usages of Writer.fixed() in practice, but in theory w.end won’t necessarily be the same as the length of the buffered contents any more (e.g. if w.seek for whatever reason is modified prior to the first write). If you care about that, then w.buffered().len is probably the most foolproof and future proof (assuming Writer.buffered() is not removed and is updated to circularly shift the buffer so that the full contents are contiguous if needed).

3 Likes