Why os.fs.cwd().createFile instead of os.fs.createFile()

Why are createFile/openFile methods of Dir instead of static functions like f.ex. os.fs.createFile()/openFile()? Not knowing the details, this seems like an unnecessary departure from filesystem APIs in other languages and run-times.


use it like this it works fine

const data = @embedFile("./deps/curse/utils.zig");

  var lines = std.mem.tokenize(u8, data, "\n");

  var n : usize  = 0;
  while(lines.next()) |line| { ..... }
1 Like

I don’t know the reason for the design decision, but for me, it’s really convenient. cwd will get me the current dir without me having to fuss about where exactly in the filesystem am I, or absolute versus relative paths, or forward versus back slashes, etc. I just get the current working dir and start creating / opening files there.

1 Like

The file will be found in your program directory.

  const file = try std.fs.cwd().createFile(
        .{ .read = true },
    defer file.close();

    const bytes_written = try file.writeAll("Hello File!");
    _ = bytes_written;

to know your base directory in which your program is located

const allocatory = std.heap.page_allocator;
    const path = try std.fs.realpathAlloc(allocatory, ".");
    defer allocatory.free(path);
    std.debug.print("{s}\n", .{path});

Perhaps this decision is inline with Zig’s philosophy to minimize “hidden” code assumptions.

This “cwd()” term makes explicit the operating system imposed assumption that non-rooted file system pathnames pssed via system calls are taken relative to the operating system’s maintained “current worknig directory” of the calling process.

1 Like

The current working directory is a pretty established part of an operating system’s API though. Some might argue that a low-level language should expose the OS API as directly as possible. For example, I find Golang’s std library far better than Python’s and to large extent I think that’s because Golang doesn’t try to hide OS details.

Some people (and I guess I fall into this camp too) tend to always use absolute filenames, never relying on the current working dir, and I find it a little weird passing absolute filenames through cwd…

1 Like

I also seem to remember there is something related to WASM in here… Maybe WASM doesn’t support relative path names, or somesuch; I don’t really recall now.

1 Like

Thanks for the replies! Digging into this a little more, I guess this exposes openat() which has the benefit that there’s not a single global working directory per process which would lead to race conditions if different threads switch to different working directories.

Quite possibly related to WASI fs too, like mentioned above. Here’s a HN thread discussing that.

1 Like

Here’s the PR where things started moving to be Dir-based instead of using free functions like std.fs.createFile:

It should now be clear the pattern that the standard library file system API is approaching. The API encourages programmers to take advantage of directory traversal via directory handles, for better general protection against Time Of Check, Time Of Use bugs across all Zig codebases.

I’m not familiar enough with how it affects Time Of Check, Time Of Use bugs to speak to its success in that, though. I think a possibly better argument is that the Dir-based API makes Zig code work with WASI by default. I’ve written a decent amount about that here:

As far as I can tell (but this is not documented anywhere, which is a problem imo and has been a point of confusion for a lot of people), the reason for having everything go through Dir is that it allows for that one API to be maximally cross-platform–even for platforms that don’t have the concept of an absolute path (e.g. WASM).

Thanks for the background, the latter issue seems pretty comprehensive!

I guess I’d find banning absolute paths in the current openFile API pretty off-putting. It seems like it’d introduce some extra “death by a thousand cuts” friction to dealing with files that’s not something I’d like to deal with when just quickly throwing stuff together. (Similar to how I sometimes feel about Zig’s “warnings-as-errors” for unused variables.) Like say passing in an absolute path to a command line tool… what’s the right response to an absolute path then? Parse it into components, wrap it into a Dir and then use that to open the file?

Yeah, I think that proposal is misguided and is (hopefully) very unlikely to be accepted. The Zig fs API just takes some getting used to but getting WASI support pretty much everywhere for free is a really nice benefit of it.