From origin (other languages) I am used to working with strings. With Zig things are less obvious for me.
How do we actually create strings or print?
We can:
use the disappearing std.BoundedArray
for creating a (short) string.
use an ArrayListUnmanaged
with heap allocation to create a string.
use format
use print
use the writer
of ArrayList
and probably many more.
There also seems some compiler magic going on with a format
function for our structs which looks like (got thus one from a video of DudeTheBuilder):
pub fn format(self: MyStruct,
comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {}
Is that documented? I could not find anything on what is going on there.
What should the type of writer
be? With anytype
we don’t know the type and available functions (no code completion).
I would be grateful if someone smarter than me is able to disect my blurry view into “The One Obvious Way To Do It”.
And also “What is the fastest way to do things?”
(In my chess-case I have to output to stdout, which can be either a terminal or a relayed stdout by some GUI).
1 Like
After writergate “The One Obvious Way To Do It" is to just write to writer.
format
method is used to convert type to a string by writing it to writer.
print
is used to do string interpolation which intern calles format
function on types if needed.
Generally you can only print
, format
to a Writer
.
Writer
allows you to write to a bunch of places including ArrayList, Terminal, GUI or anything else and this is the whole point of Writer
.
Also format function is much nicer now:
3 Likes
floooh
August 12, 2025, 1:58pm
3
It’s interesting because in the ‘printf’ Zig override I have in the sokol Zig bindings I had an (old-style) writer before, but now just replaced that with fmt.bufPrint
into a buffer (that buffer is the exact same where the C vprintf() equivalent is doing it’s formatted printing to, so feature wise there’s no difference).
This is the new code:
// std.fmt-style formatted print
pub fn print(comptime fmt: anytype, args: anytype) void {
const cbuf = getClearedFmtBuffer();
const p: [*]u8 = @constCast(@ptrCast(cbuf.ptr));
const buf = p[0..cbuf.size];
const out = @import("std").fmt.bufPrint(buf, fmt, args) catch "";
for (out) |c| putc(c);
}
(and before you mention it, that “putc” at the end is not the C stdlib putc but a very lightweight “copy this byte into a buffer” function - and it’s not different from what the old writer did).
The old writer implementation looked like this (needless to say, I like the bufPrint()
version a lot more
// std.fmt compatible Writer
pub const Writer = struct {
pub const Error = error{};
pub fn writeAll(self: Writer, bytes: []const u8) Error!void {
_ = self;
for (bytes) |byte| {
putc(byte);
}
}
pub fn writeByteNTimes(self: Writer, byte: u8, n: usize) Error!void {
_ = self;
var i: u64 = 0;
while (i < n) : (i += 1) {
putc(byte);
}
}
pub fn writeBytesNTimes(self: Writer, bytes: []const u8, n: usize) Error!void {
var i: usize = 0;
while (i < n) : (i += 1) {
try self.writeAll(bytes);
This file has been truncated. show original
ericlang:
There also seems some compiler magic going on with a format
function for our structs which looks like (got thus one from a video of DudeTheBuilder):
pub fn format(self: MyStruct,
comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {}
Is that documented? I could not find anything on what is going on there.
What should the type of writer
be? With anytype
we don’t know the type and available functions (no code completion).
Not compiler magic, just comptime magic From the std.fmt.format
docs:
If a formatted user type contains a function of the type
pub fn format(value: ?, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void
with ?
being the type formatted, this function will be called instead of the default implementation. This allows user types to be formatted in a logical manner instead of dumping all fields of the type.
A user type may be a struct
, vector
, union
or enum
type.
The actual implementation of this is dead simple (in std.fmt.formatType
):
if (std.meta.hasMethod(T, "format")) {
return try value.format(actual_fmt, options, writer);
}
(Note that this is looking like it will move to std.Io.Writer.print
in 0.15)
1 Like
also format options have been updated:
t
for tag names of enums or tagged unions
b64
to display bytes/strings in base 64
D
to output nanosecconds in a human readable format
or custom duration (formatDuration
fn with same signature as custom format)
B
to output bytes in human readable SI
units (decimal)
Bi
to output bytes in human readable IEC
units (binary)
B
and Bi
accept custom format functions too formatByteSize
with an additional ByteSizeUnits
arg that can be comptime
f
is now required to use a types custom format function
looks like most options can use a custom format fn with some variations in arguments, i got tired of listing them.
The format function is simpler now:
fn (*Writer, args: anytype) Writer.Error!void
though in the implementation it doesn’t pass it args
, only the writer, so not sure what that’s about, got this from docs.
1 Like
Is there anything like Python’s __str__
and __repr__
which are used to format objects in a human -readable or (ideally) in a source-code like way?
I haven’t tried it, but it looks like two different string forms for a struct could be achieved with the old format function, but not with the new.
If that’s true, I don’t really care much, but it’s something to consider.
Sze
August 13, 2025, 6:50pm
7
std.fmt.alt allows you to call format functions with different names (which aren’t called format).
3 Likes
adria
August 14, 2025, 12:02am
8
Here’s the approach I am using in zignal :
The Image
struct has a format
method that outputs a human readable string. Something like:
Image(Rgba){ .rows = 1024, .cols = 1024 }
/// Formats the image using the best available terminal format.
/// Automatically tries sixel with sensible defaults, falling back to ANSI blocks if needed.
/// For explicit control over output format, use the display() method instead.
pub fn format(self: Self, writer: *std.Io.Writer) std.Io.Writer.Error!void {
const type_name: []const u8 = @typeName(T);
if (std.mem.lastIndexOfScalar(u8, type_name, '.')) |pos| {
try writer.print("Image({s}){{ .rows = {d}, .cols = {d} }}", .{ type_name[pos + 1 ..], self.rows, self.cols });
} else {
try writer.print("Image({s}){{ .rows = {d}, .cols = {d} }}", .{ type_name, self.rows, self.cols });
}
}
Image
has also a display
method, that returns a DisplayFormatter
struct, which, in turn, has a format
method. This method is in charge of displaying the image to the terminal, using the specified protocol (or auto detect the best supported one).
pub fn display(self: *const Self, display_format: DisplayFormat) DisplayFormatter(T) {
return DisplayFormatter(T){
.image = self,
.display_format = display_format,
};
}
/// Formatter struct for terminal display with progressive degradation
pub fn DisplayFormatter(comptime T: type) type {
return struct {
image: *const Image(T),
display_format: DisplayFormat,
const Self = @This();
pub fn format(self: Self, writer: *std.Io.Writer) std.Io.Writer.Error!void {
This allows me to do stuff like this:
const std = @import("std");
const zignal = @import("zignal");
const Image = zignal.Image(zignal.Rgba);
pub fn main() !void {
var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
defer _ = debug_allocator.deinit();
const gpa = debug_allocator.allocator();
var image: Image = try .load(gpa, "../assets/liza.jpg");
defer image.deinit(gpa);
std.debug.print("{f}\n", .{image});
std.debug.print("{f}\n", .{image.display(.auto)});
}
Which outputs:
I got all this info from reading this PR:
master
← writergate
opened 03:33AM - 04 Jul 25 UTC
[Previous Scandal](https://github.com/ziglang/zig/pull/10055)
## Summary
D… eprecates all existing std.io readers and writers in favor of the newly provided `std.Io.Reader` and `std.Io.Writer` which are *non-generic* and have the buffer above the vtable - in other words the buffer is **in the interface, not the implementation**. This means that although Reader and Writer are no longer generic, they are still transparent to optimization; all of the interface functions have a concrete hot path operating on the buffer, and only make vtable calls when the buffer is full.
I have a lot more changes to upstream but it was taking too long to finish them so I decided to do it more piecemeal. Therefore, I opened this tiny baby PR to get things started.
These changes are extremely breaking. I am sorry for that, but I have carefully examined the situation and acquired confidence that this is the direction that Zig needs to go. I hope you will strap in your seatbelt and come along for the ride; it will be worth it.
The breakage in this first PR mainly has to do with formatted printing.
## More Detailed Motivation
I wrote this up [for ziggit](https://ziggit.dev/t/libucw-fastbufs-are-similar-to-the-new-zig-io-interface-but-30-years-older/11330/4?u=andrewrk) but I think it would be good to include in release notes:
* The old interface was generic, poisoning structs that contain them and forcing all functions to be generic as well with `anytype`. The new interface is concrete.
* Bonus: the concreteness removes temptation to make APIs operate directly on networking streams, file handles, or memory buffers, giving us a more reusable body of code. For example, `http.Server` after the change no longer depends on `std.net` - it operates only on streams now.
* The old interface passed errors through rather than defining its own set of error codes. This made errors in streams about as useful as `anyerror`. The new interface carefully defines precise error sets for each function with actionable meaning.
* The new interface has the buffer in the interface, rather than as a separate "BufferedReader" / "BufferedWriter" abstraction. This is more optimizer friendly, particularly for debug mode.
* The new interface supports high level concepts such as vectors, splatting, and direct file-to-file transfer, which can propagate through an entire graph of readers and writers, reducing syscall overhead, memory bandwidth, and CPU usage.
* The new interface has "peek" functionality - a buffer awareness that offers API convenience for the user as well as simplicity for the implementation.
## Performance Data
### Building Self-Hosted Compiler with Itself
```
Benchmark 1 (3 runs): master/fast/bin/zig build-exe ...
measurement mean ± σ min … max outliers delta
wall_time 12.4s ± 49.3ms 12.3s … 12.4s 0 ( 0%) 0%
peak_rss 1.03GB ± 4.67MB 1.02GB … 1.03GB 0 ( 0%) 0%
cpu_cycles 105G ± 323M 105G … 105G 0 ( 0%) 0%
instructions 207G ± 4.41M 207G … 207G 0 ( 0%) 0%
cache_references 6.62G ± 23.9M 6.60G … 6.64G 0 ( 0%) 0%
cache_misses 449M ± 3.17M 447M … 453M 0 ( 0%) 0%
branch_misses 411M ± 1.62M 409M … 412M 0 ( 0%) 0%
Benchmark 2 (3 runs): writergate/fast/bin/zig build-exe ...
measurement mean ± σ min … max outliers delta
wall_time 10.6s ± 28.6ms 10.5s … 10.6s 0 ( 0%) ⚡- 14.6% ± 0.7%
peak_rss 1.14GB ± 5.26MB 1.14GB … 1.15GB 0 ( 0%) 💩+ 10.8% ± 1.1%
cpu_cycles 95.0G ± 19.8M 95.0G … 95.1G 0 ( 0%) ⚡- 9.6% ± 0.5%
instructions 191G ± 2.22M 191G … 191G 0 ( 0%) ⚡- 7.8% ± 0.0%
cache_references 5.68G ± 13.9M 5.66G … 5.69G 0 ( 0%) ⚡- 14.2% ± 0.7%
cache_misses 386M ± 2.47M 384M … 388M 0 ( 0%) ⚡- 14.2% ± 1.4%
branch_misses 400M ± 516K 400M … 401M 0 ( 0%) ⚡- 2.6% ± 0.7%
```
### Building My Music Player Project
[source](https://codeberg.org/andrewrk/groovebasin)
```
Benchmark 1 (3 runs): master/stage3/bin/zig build-exe ...
measurement mean ± σ min … max outliers delta
wall_time 1.86s ± 36.3ms 1.84s … 1.90s 0 ( 0%) 0%
peak_rss 798MB ± 3.84MB 796MB … 803MB 0 ( 0%) 0%
cpu_cycles 11.0G ± 24.0M 11.0G … 11.1G 0 ( 0%) 0%
instructions 28.5G ± 796K 28.5G … 28.5G 0 ( 0%) 0%
cache_references 610M ± 1.41M 609M … 611M 0 ( 0%) 0%
cache_misses 52.8M ± 559K 52.2M … 53.2M 0 ( 0%) 0%
branch_misses 49.7M ± 366K 49.3M … 50.1M 0 ( 0%) 0%
Benchmark 2 (3 runs): writergate/bin/zig build-exe ...
measurement mean ± σ min … max outliers delta
wall_time 2.09s ± 2.02ms 2.09s … 2.09s 0 ( 0%) 💩+ 12.4% ± 3.1%
peak_rss 800MB ± 693KB 800MB … 801MB 0 ( 0%) + 0.3% ± 0.8%
cpu_cycles 12.4G ± 41.7M 12.4G … 12.4G 0 ( 0%) 💩+ 12.5% ± 0.7%
instructions 30.4G ± 472K 30.4G … 30.4G 0 ( 0%) 💩+ 6.8% ± 0.0%
cache_references 615M ± 2.47M 612M … 617M 0 ( 0%) + 0.9% ± 0.7%
cache_misses 53.9M ± 1.11M 52.9M … 55.1M 0 ( 0%) + 2.1% ± 3.8%
branch_misses 46.5M ± 179K 46.4M … 46.7M 0 ( 0%) ⚡- 6.3% ± 1.3%
```
### Compiler Binary Size (ReleaseSmall)
* x86_64: 13.6 -> 13.3 MiB (-2%)
* zig1.wasm: 2.8 -> 2.7 MiB (-4%)
### C Backend Building the Zig Compiler
```
Benchmark 1 (3 runs): master/bin/zig build-exe -ofmt=c ...writergate source tree...
measurement mean ± σ min … max outliers delta
wall_time 2.67s ± 40.5ms 2.65s … 2.72s 0 ( 0%) 0%
peak_rss 572MB ± 1.13MB 571MB … 573MB 0 ( 0%) 0%
cpu_cycles 37.3G ± 190M 37.2G … 37.5G 0 ( 0%) 0%
instructions 72.8G ± 3.21M 72.8G … 72.8G 0 ( 0%) 0%
cache_references 1.90G ± 9.27M 1.89G … 1.91G 0 ( 0%) 0%
cache_misses 131M ± 1.29M 130M … 132M 0 ( 0%) 0%
branch_misses 146M ± 161K 146M … 146M 0 ( 0%) 0%
Benchmark 2 (3 runs): writergate/bin/zig build-exe -ofmt=c ...writergate source tree...
measurement mean ± σ min … max outliers delta
wall_time 2.70s ± 19.5ms 2.68s … 2.72s 0 ( 0%) + 0.8% ± 2.7%
peak_rss 572MB ± 208KB 572MB … 572MB 0 ( 0%) - 0.0% ± 0.3%
cpu_cycles 36.4G ± 253M 36.2G … 36.6G 0 ( 0%) - 2.3% ± 1.4%
instructions 69.8G ± 4.18M 69.8G … 69.8G 0 ( 0%) ⚡- 4.1% ± 0.0%
cache_references 2.08G ± 38.2M 2.04G … 2.12G 0 ( 0%) 💩+ 9.5% ± 3.3%
cache_misses 134M ± 2.81M 131M … 136M 0 ( 0%) + 2.1% ± 3.8%
branch_misses 143M ± 486K 142M … 143M 0 ( 0%) ⚡- 2.5% ± 0.6%
```
### C Backend Building Hello World
ReleaseFast zig
```
Benchmark 1 (27 runs): master/stage3/bin/zig build-exe master/zig/test/standalone/simple/hello_world/hello.zig -ofmt=c
measurement mean ± σ min … max outliers delta
wall_time 187ms ± 4.08ms 180ms … 194ms 0 ( 0%) 0%
peak_rss 129MB ± 553KB 129MB … 131MB 0 ( 0%) 0%
cpu_cycles 1.49G ± 11.3M 1.47G … 1.53G 2 ( 7%) 0%
instructions 2.70G ± 252K 2.70G … 2.70G 0 ( 0%) 0%
cache_references 101M ± 498K 101M … 103M 1 ( 4%) 0%
cache_misses 8.70M ± 194K 8.14M … 9.18M 2 ( 7%) 0%
branch_misses 8.32M ± 91.4K 8.07M … 8.46M 1 ( 4%) 0%
Benchmark 2 (35 runs): writergate/bin/zig build-exe writergate/zig/test/standalone/simple/hello_world/hello.zig -ofmt=c
measurement mean ± σ min … max outliers delta
wall_time 145ms ± 5.17ms 137ms … 160ms 0 ( 0%) ⚡- 22.4% ± 1.3%
peak_rss 128MB ± 518KB 127MB … 129MB 0 ( 0%) - 0.8% ± 0.2%
cpu_cycles 1.17G ± 11.4M 1.16G … 1.20G 0 ( 0%) ⚡- 21.5% ± 0.4%
instructions 2.07G ± 210K 2.07G … 2.07G 0 ( 0%) ⚡- 23.3% ± 0.0%
cache_references 81.4M ± 478K 80.5M … 82.6M 0 ( 0%) ⚡- 19.7% ± 0.2%
cache_misses 7.21M ± 152K 6.91M … 7.47M 0 ( 0%) ⚡- 17.1% ± 1.0%
branch_misses 7.40M ± 64.0K 7.27M … 7.54M 0 ( 0%) ⚡- 11.1% ± 0.5%
```
Debug zig
```
Benchmark 1 (3 runs): master/bin/zig build-exe master/zig/test/standalone/simple/hello_world/hello.zig -ofmt=c
measurement mean ± σ min … max outliers delta
wall_time 17.2s ± 16.7ms 17.1s … 17.2s 0 ( 0%) 0%
peak_rss 245MB ± 1.04MB 244MB … 246MB 0 ( 0%) 0%
cpu_cycles 54.4G ± 150M 54.2G … 54.5G 0 ( 0%) 0%
instructions 40.8G ± 3.62M 40.8G … 40.8G 0 ( 0%) 0%
cache_references 2.65G ± 5.59M 2.65G … 2.66G 0 ( 0%) 0%
cache_misses 408M ± 1.97M 406M … 410M 0 ( 0%) 0%
branch_misses 190M ± 103K 190M … 190M 0 ( 0%) 0%
Benchmark 2 (3 runs): writergate/bin/zig build-exe writergate/zig/test/standalone/simple/hello_world/hello.zig -ofmt=c
measurement mean ± σ min … max outliers delta
wall_time 16.1s ± 43.4ms 16.0s … 16.1s 0 ( 0%) ⚡- 6.3% ± 0.4%
peak_rss 244MB ± 695KB 244MB … 245MB 0 ( 0%) - 0.1% ± 0.8%
cpu_cycles 44.3G ± 139M 44.2G … 44.4G 0 ( 0%) ⚡- 18.5% ± 0.6%
instructions 33.4G ± 437K 33.4G … 33.4G 0 ( 0%) ⚡- 18.2% ± 0.0%
cache_references 2.19G ± 11.5M 2.18G … 2.20G 0 ( 0%) ⚡- 17.2% ± 0.8%
cache_misses 339M ± 6.31M 332M … 345M 0 ( 0%) ⚡- 16.9% ± 2.6%
branch_misses 185M ± 395K 184M … 185M 0 ( 0%) ⚡- 2.7% ± 0.3%
```
## Upgrade Guide
Turn on `-freference-trace` to help you find all the format string breakage.
### `"{f}"` Required to Call `format` Methods
Example:
```zig
std.debug.print("{}", .{std.zig.fmtId("example")});
```
This will now cause a compile error:
```
error: ambiguous format string; specify {f} to call format method, or {any} to skip it
```
Fixed by:
```zig
std.debug.print("{f}", .{std.zig.fmtId("example")});
```
Motivation: eliminate these two footguns:
Introducing a `format` method to a struct caused a bug if there was formatting code somewhere that prints with {} and then starts rendering differently.
Removing a `format` method to a struct caused a bug if there was formatting code somewhere that prints with {} and is now changed without notice.
Now, introducing a `format` method will cause compile errors at all `{}` sites. In the future, it will have no effect.
Similarly, eliminating a `format` method will not change any sites that use `{}`.
Using `{f}` always tries to call a `format` method, causing a compile error if none exists.
### Format Methods No Longer Have Format Strings or Options
```zig
pub fn format(
this: @This(),
comptime format_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void { ... }
```
:arrow_down:
```zig
pub fn format(this: @This(), writer: *std.io.Writer) std.io.Writer.Error!void { ... }
```
The deleted FormatOptions are now for numbers only.
Any state that you got from the format string, there are three suggested alternatives:
1. different format methods
```zig
pub fn formatB(foo: Foo, writer: *std.io.Writer) std.io.Writer.Error!void { ... }
```
This can be called with `"{f}", .{std.fmt.alt(Foo, .formatB)}`.
2. `std.fmt.Alt`
```zig
pub fn bar(foo: Foo, context: i32) std.fmt.Alt(F, F.baz) {
return .{ .data = .{ .context = context } };
}
const F = struct {
context: i32,
pub fn baz(f: F, writer: *std.io.Writer) std.io.Writer.Error!void { ... }
};
```
This can be called with `"{f}", .{foo.bar(1234)}`.
3. return a struct instance that has a format method, combined with `{f}`.
```zig
pub fn bar(foo: Foo, context: i32) F {
return .{ .context = 1234 };
}
const F = struct {
context: i32,
pub fn format(f: F, writer: *std.io.Writer) std.io.Writer.Error!void { ... }
};
```
This can be called with `"{f}", .{foo.bar(1234)}`.
### Formatted Printing No Longer Deals with Unicode
If you were relying on alignment combined with Unicode codepoints, it is now ASCII/bytes only. The previous implementation was not fully Unicode-aware. If you want to align Unicode strings you need full Unicode support which the standard library does not provide.
### std.io.getStdOut().writer().print()
Please use buffering! And **don't forget to flush**!
```zig
var stdout_buffer: [1024]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&buffer);
const stdout = &stdout_writer.interface;
// ...
try stdout.print("...", .{});
// ...
try stdout.flush();
```
### Miscellaneous
* std.fs.File.reader -> std.fs.File.deprecatedReader
* std.fs.File.writer -> std.fs.File.deprecatedWriter
* std.fmt.fmtSliceEscapeLower -> std.ascii.hexEscape
* std.fmt.fmtSliceEscapeUpper -> std.ascii.hexEscape
* std.zig.fmtEscapes -> std.zig.fmtString
* std.fmt.fmtSliceHexLower -> {x}
* std.fmt.fmtSliceHexUpper -> {X}
* std.fmt.fmtIntSizeDec -> {B}
* std.fmt.fmtIntSizeBin -> {Bi}
* std.fmt.fmtDuration -> {D}
* std.fmt.fmtDurationSigned -> {D}
* std.fmt.Formatter -> std.fmt.Alt
- now takes context type explicitly
- no fmt string
These are deprecated but not deleted yet:
* std.fmt.format -> std.io.Writer.print
* std.io.GenericReader -> std.io.Reader
* std.io.GenericWriter -> std.io.Writer
* std.io.AnyReader -> std.io.Reader
* std.io.AnyWriter -> std.io.Writer
If you have an old stream and you need a new one, you can use `adaptToNewApi()` like this:
```zig
fn foo(old_writer: anytype) !void {
var adapter = old_writer.adaptToNewApi();
const w: *std.io.Writer = &adapter.new_interface;
try w.print("{s}", .{"example"});
// ...
}
```
## New API
### Formatted Printing
* {t} is shorthand for `@tagName()` and `@errorName()`
* {d} and other integer printing can be used with custom types which calls `formatNumber` method.
* {b64}: output string as standard base64
### `std.io.Writer` and `std.io.Reader`
These have a bunch of handy new APIs that are more convenient, perform better, and are not generic. For instance look at how reading until a delimiter works now.
These streams also feature some unique concepts compared with other languages' stream implementations:
* The concept of **discarding** when reading: allows efficiently ignoring data. For instance a decompression stream, when asked to discard a large amount of data, can skip decompression of entire frames.
* The concept of **splatting** when writing: this allows a logical "memset" operation to pass through I/O pipelines without actually doing any memory copying, turning an O(M*N) operation into O(M) operation, where M is the number of streams in the pipeline and N is the number of repeated bytes. In some cases it can be even more efficient, such as when splatting a zero value that ends up being written to a file; this can be lowered as a seek forward.
* Sending a file when writing: this allows an I/O pipeline to do direct fd-to-fd copying when the operating system supports it.
* The stream user provides the buffer, but the stream implementation decides the minimum buffer size. This effectively moves state from the stream implementation into the user's buffer
### `std.fs.File.Reader`
Memoizes key information about a file handle such as:
* The size from calling stat, or the error that occurred therein.
* The current seek position.
* The error that occurred when trying to seek.
* Whether reading should be done positionally or streaming.
* Whether reading should be done via fd-to-fd syscalls (e.g. `sendfile`)
versus plain variants (e.g. `read`).
Fulfills the `std.io.Reader` interface.
This API turned out to be super handy in practice. Having a concrete type to pass around that memoizes file size is really nice.
### `std.fs.File.Writer`
Same idea but for writing.
## What's NOT Included in this Branch
This is part of a series of changes leading up to "I/O as an Interface" and Async/Await Resurrection. However, this branch does not do any of that. It also does not do any of these things:
* Rework tls
* Rework http
* Rework json
* Rework zon
* Rework zstd
* Rework flate
* Rework zip
* Rework package fetching
* Delete fifo.LinearFifo
* Delete the deprecated APIs mentioned above
I have done all the above in a separate branch and plan to upstream them one at a time in follow-up PRs, eliminating dependencies on the old streaming APIs like a game of pick-up-sticks.
## Merge Checklist:
* [x] bootstrapped compiler is crashing
* [x] Windows TODOs
* [x] fix bootstrapped stage3 compiler crashing
* [x] fix failing behavior tests
* [x] fix failing std lib tests
* [x] solve the TODOs in std.io.Writer
* [x] finish implementing std.fs.File.Writer which doesn't handle positional mode. this is probably breaking caching which now relies on the manifest being written positionally to save from having to seek.
* [x] eliminate Writer.count
* [x] update-zig1 is non viable
* [x] error for using alignment options when they're not observed
* [x] something about packed structs in Reader
* [x] formatNumber rather than formatInteger
2 Likes
Aha aha that clears things up!
Ashok
August 14, 2025, 5:48am
10
I think it would be great to mark some features that will get future changes in the documentation itself. That way we can isolate those features and implement the changes in our personal projects.
Recently I had a tough time figuring out how to use bufferedWriter - finally found out that it was removed. For starters like me it may be little intimidating.
Of course changes are expected but just putting a tentative mark in the documentation will help a lot.
Anyway great language though - great speed !
Thank you
Reading the “writergate” of Andrew I get the feeling I need to start learning Zig all over again.
Anyway: thanks for all responses. I know a bit where to look now.
I think I will wait with programming any further until 0.15 is there.