How to use AnyWriter

I have a tagged union that is being used for static dispatch calls to a function named inspect. inspect is simply writing to a Writer. However, I have two scenarios when it is being called: once for the production code using std.io.getStdOut().writer() and once for unit test using std.ArrayList.writer(). Here’s snippet of code for context:

pub const Object = union(enum) {
    integer: Integer,
    // ... many more variants

    pub fn inspect(self: *const Object, writer: anytype) anyerror!void {
        switch (self.*) {
            inline else => |obj| try obj.inspect(writer),
        }
    }
};
pub const Integer = struct {
    value: i64,

    pub fn inspect(self: Integer, writer: anytype) anyerror!void {
        return try writer.print("{d}", .{self.value});
    }
};
// ... many more struct to implement variants

The code above both compiles and passes unit test. However, while writing this code out, I stumbled upon this comment in std.io:

/// Deprecated; consider switching to `AnyReader` or use `GenericReader`
/// to use previous API.
pub const Reader = GenericReader;
/// Deprecated; consider switching to `AnyWriter` or use `GenericWriter`
/// to use previous API.
pub const Writer = GenericWriter;

pub const AnyReader = @import("io/Reader.zig");
pub const AnyWriter = @import("io/Writer.zig");

Hmm… ok, it seems like the standard library authors are nudging the community towards using AnyWriter. I began implementing the changes for AnyWriter, but I can’t seem to get the code compiling. I’m met with the following compile error:

error: expected type 'io.Writer', found 'io.GenericWriter(*array_list.ArrayListAligned(u8,null),error{OutOfMemory},(function 'appendWrite'))'
        try object_test.object.inspect(writer);

Any pointers on how to introduce AnyWriter? Or is AnyWriter even the correct tool to solve this problem?

I’m mainly trying to help out ZLS by giving it more compile-time information, since I already have a functioning solution mentioned above. Any constructive criticism is always welcome!

For your use case where you just have two different writer types you need to support, using anytype is completely fine.

If you wanted to use AnyWriter, then it would just take changing the functions’ writer parameter to AnyWriter and the rest of the implementation would stay the same. Then you would use std.io.getStdOut().writer().any() and std.ArrayList(u8).writer().any() as arguments.

Without code it’s hard to tell what’s causing that error.

2 Likes

.any() was exactly what I was looking for! I’ll have to dig into the source code to understand the secret sauce.

P.S. I love the profile pic! I grew up on Paper Mario.

1 Like

Yes, it’s important to understand the tradeoffs you’re making when deciding between anytype and AnyWriter/Reader .

anytype is static dispatch, it has no runtime overhead but when used with many different writer types it can bloat your program’s size by having to generate the same function for each type you give it.

AnyWriter is dynamic dispatch, and has some runtime overhead since it’s basically a function pointer + context pointer.

1 Like