Difference between importing files vs structs

My module is structured like the standard library.

For example:

module
├── mailbox
│   ├── coe
│   │   ├── client.zig
│   │   └── server.zig
│   ├── coe.zig
│   └── design.md
├── mailbox.zig
├── pdi.zig
├── Port.zig
├── root.zig

It has a root.zig at the module root.

And it has a file for each subfolder that defines the imports.

mailbox.zig:

pub const coe = @import("mailbox/coe.zig");

For example: mailbox.zig corresponds to the directory mailbox/.

My question is, should I use file-based imports:

const mailbox = @import("mailbox.zig");
const coe = @import("mailbox/coe.zig");

or struct-based imports?

const mailbox = @import("mailbox.zig");
const coe = mailbox.coe

There’s no difference; pick whichever you like better.

I think there is a difference in how I am able to hide API’s from the user?

If I only use the struct based imports, then I cannot hide any API’s from the user. But if I use file based imports, then I can.

For example:

// not hidden from user
pub const coe = @import("mailbox/coe.zig");

// hidden from user
const coe = @import("mailbox/coe.zig");

And if I need to have more files in my library reference coe then I need to use file-based imports?

I have been using file based imports up until this point, but I have been questioning this decision because I think I want the majority of my code to look like how a user would use it, which is only struct-based imports:

const coe = @import("gatorcat").mailbox.coe;

In zig files ARE structs.

Whether you treat them as structs or namespaces doesn’t change that.

I’m guessing you’re conflating declarations and fields.

Fields can’t be private, they are always public and to access them you need an instance of the type.

Declarations are const’s var’s and functions, they are global, i.e. not tied to an instance of the type, as such you don’t need an instance to access them.
Declarations can be made public with the pub keyword, allowing them to be accessed outside the type they are declared in.

2 Likes

I use similar re-export files within a ‘subsystem directory’ (e.g. chipz/src/chips/chips.zig at main · floooh/chipz · GitHub), but then I define per-subsystem-modules in the project’s build.zig, e.g.:

…and then all ‘cross-system-access’ happens via module-imports, e.g.:

IIRC you can’t import zig files via the parent directory anyway (e.g. const blub = @import("../bla/blub.zig") so wrapping everything into modules (which then depend on each other) is the cleaner approach anyway - and this also enforces a nice and clean hierarchical structure in your project (instead of just importing anything from anywhere).

If you provide your library as a zig module, you can’t offer the “file imports” option anyways. It only makes sense within a module scope.

You can as long as both files are part of the same module.
I think that bla has to be a subdirectory/underneath where the root file of the module is located, so it can’t be one of the sibling directories of the parent directory of the root file.

1 Like