File naming conventions

Zig’s style guide suggests using two different styles for file names:

If the file (implicitly a struct) has top level fields, it should be named like any other struct with fields using TitleCase . Otherwise, it should use snake_case .

In general, I think this makes a lot of sense, except that there are files which logically are not structs.
Now, of course all .zig files are actually structs, but I find myself putting enums or unions into their own files and I’ve always struggled to name these files in a way that is logical and clear to the user. Same problem applies to generic types, btw, which are functions of course.

Let’s suppose I had a huge enum of different event types:

const Event = enum { ... };

At some point I might want to put just that enum into its own file. What do I name that file? As per the style guide, it should be event.zig (or at least something in snake_case). But then when I want to use Event I either need to include the file into a constant event and then always write event.Event, which is very silly, or I need to do this thing:

const Event = @import("event.zig").Event;

But how do I know to do that? As a consumer of that file, I always need to look inside of it to see how to import it properly.

I don’t like that.

That’s why I came up with a slightly different naming convention for myself and I’d like to share that with y’all.

When I create a file with no fields and exactly one public member, I name the file @typeName(T) ++ "_.zig"! So our Event enum from earlier would live in a file named Event_.zig. When importing this file, you now know immediately that you shouldn’t just import the entire file into a constant but rather directly access a member of the same name. So the import would look like this now:

const Event = @import("Event_.zig").Event;

Much clearer!

This convention works for enums, unions, functions, or anything else really. And since the underscore is at the end of the file name, the sorting between files is not affected.

What do y’all think about this? Do you have your own convention for this? Can you spot any problems with mine? Do you consider this worth it or not? Let’s talk about it :slight_smile:

1 Like

The best way to understand what something is and how to use it, you look at the file. Absolutely nothing wrong with that.

There’s a good argument that you shouldn’t import anything you haven’t looked over, regardless of the programming language.

1 Like

That’s interesting. I almost feel like if you use this convention, then it would be more consistent to not use top-level fields for single-struct files at all, which would allow you to omit the “_”, as you would now know that any file not in snake case would require an additional .Name after @Import("Name.zig").

I’d be hesitant to use this convention since it doesn’t follow the naming guidelines (as pedantic as that may sound), but you’re right that it’s hard to have an elegant convention here without breaking the rules.

However, all of this can be solved if you simply have an additional namespace that contains all of these single-item files, like zig does with std, allowing you to simply call std.AutoHashMap, or do const AutoHashMap = std.AutoHashMap if you prefer.

That’s a fair point but I don’t want to have to re-read all of my files over and over again because I’ve forgotten how exactly they’re structured inside. Maybe that’s a me-problem but this happens constantly to me, especially after taking a break. I know the code is safe and good, I just don’t remember what the structure was exactly.

Completely avoiding fields in files is what I’ve been doing previously. It works, and it’s more consistent, and it actually fully follows the style guide… but it’s very inconvenient. Quite a lot of my files end up having fields inside them and this just added boilerplate to every single import of those files, which wasn’t really needed. I might go back to it eventually, who knows, but for the time being I prefer this convention, I think :slight_smile:

Is there a reason that the namespace approach wouldn’t work for you? You would just need a single namespace for your entire project/library, and could import said namespace in every file (since zig allows import cycles).

Also, if you are making a package, it will allow your in-package code to be more consistent with code outside of the package, as both in-package and outside code will be using a parent namespace to access the package contents.

Now, if you are making a program that won’t be a dependency and has a simple code file structure (just a few files and no directories), then I understand that making a namespace could be overkill, but I think the approach that std takes is the intended way to solve your problem.

To be honest, it just feels weird to import files within the same namespace by referring to a namespace. What would I even call it? private? Feels weird to me. I might try it in the future though, so thanks!

  • If you are making a package, use the name of the package.
  • If you are making an application, use the name of the application.
  • If you are making a project that doesn’t have a concrete name… I’m not sure. I would probably use something generic like lib, app, or project.
1 Like