Recent changes to `std.fs.Dir.openDir` when iterating?

I have code like this which opens a directory and walks over its contents, recursively:

var dir: std.fs.Dir = try std.fs.cwd().openDir(path, .{ .iterate = true });
defer dir.close();
var iterable_dir = dir.iterate();
while (try iterable_dir.next()) |e| {
    switch (e.kind) {
        .file => {}, // handle a file
        else => {},  // handle a directory
    }
}

This worked fine with 0.12.0, and returned all its contents, recursively, for all depths; but after I upgraded to 0.13.0, it is only returning entries in the current directory (it does not seem to recurse anymore). I did not find an obvious reference in the docs – can anybody point me to anything obvious? Did these APIs change in 0.13.0?

Cheers,
Gonzo

Try using std.fs.Dir.walk, smth like this should work:

var walker = try dir.walk(allocator);
defer walker.deinit();
while (try walker.next()) |e| {
    ...
}

Cool, I can try that. In fact, it wasn’t obvious to me why there is an iterate() and a walk() function.

Does this imply that iterate() is deprecated? Can you point me to docs stating this?

Thanks!

I can confirm walk() works, thanks @tensorush.

1 Like

The distinction is that iterate() goes over directory’s immediate contents, while walk() uses iterate() and recurses into directory’s contents.

That might be a recent change then – I had been using iterate() for months, iterating recursively into a directory.

1 Like

I checked recent PRs but could not find anything after a quick look.

Yeah I don’t think dir.iterate was ever recursive, are you seeing a case where it actually was?

Yes, absolutely. The code snippet I wrote recursed into the directory – perhaps it was not intended?

As I mentioned, the walker iterator works recursively for me.

As you can see, whether I run this on 0.12.0 or 0.13.0 it doesn’t recurse:

C:\temp\testdir>touch foo
touch foo

C:\temp\testdir>mkdir subdir
mkdir subdir

C:\temp\testdir>touch subdir/bar
touch subdir/bar

C:\temp\testdir>zigup run 0.12.0 run iterate.zig
zigup run 0.12.0 run iterate.zig
info: file: foo
info: file: iterate.zig
info: directory: subdir

C:\temp\testdir>zigup run 0.13.0 run iterate.zig
zigup run 0.13.0 run iterate.zig
info: file: foo
info: file: iterate.zig
info: directory: subdir

Here’s iterate.zig:

const std = @import("std");

pub fn main() !void {
    var dir: std.fs.Dir = try std.fs.cwd().openDir(".", .{ .iterate = true });
    defer dir.close();
    var iterable_dir = dir.iterate();
    while (try iterable_dir.next()) |e| {
        std.log.info("{s}: {s}", .{@tagName(e.kind), e.name});
    }
}

Could you be more specific about what you mean? Did the name field of the returned Entry contain a path separator? What platform was this happening on?

AFAIK Dir.Iterator has not changed for multiple releases and it’s not really possible for it to be recursive, since all implementations are just calling the platform’s getdents equivalent with the Dir’s fd.

You are both right @marler8997 and @squeek502 – I was the one who broke it. My original old code was:

var dir: std.fs.Dir = try std.fs.cwd().openDir(path, .{});
defer dir.close();
var iterable_dir = try dir.openIterableDir(".", .{});
defer iterable_dir.close();
var dir_walker = try iterable_dir.walk(self.allocator);
defer dir_walker.deinit();
while (try dir_walker.next()) |e| {
  // ...
}

That old code recursed into all subdirectories. But when I upgraded the code to the newer zig version, I mistakenly dropped the walker and changed the code to this:

// ...
var iterable_dir = dir.iterate();
while (try iterable_dir.next()) |e| {
  // ...
}

After @tensorush’s suggestion, I restored the walker; you have now also restored my sanity.

Thanks everyone for the assist.

2 Likes