Imports
const std = @import("std");
const expect = std.testing.expect;
// A relative path imports another source file. The result is a struct type
// whose declarations are that file's `pub` declarations.
const shapes = @import("_shapes.zig");
test "use an imported declaration" {
try expect(shapes.double(21) == 42);
}
test "imported types work like any other" {
const c = shapes.Circle{ .radius = 2 };
try expect(c.area() > 12.5 and c.area() < 12.6);
}
test "a file is a struct" {
// `@This()` inside a file refers to that file's implicit struct type,
// which is why `@import` can return something you access with `.`.
try expect(@TypeOf(shapes) == type);
try expect(shapes.pi > 3.14);
}
test "std is just another import" {
// `@import("std")` is the same mechanism, resolved by the compiler
// rather than by path.
try expect(std.mem.eql(u8, "a", "a"));
}@import takes a path and returns that file as a struct type:
const shapes = @import("_shapes.zig");
shapes.Circle{ .radius = 2 };
There is no separate module system to learn. A file is a struct, @import
evaluates it, and you access its declarations with . like any other struct.
pub is the boundary
Only pub declarations are visible to importers. Everything else is private to
the file:
pub const pi = 3.14159; // visible
const secret = 42; // not
This is the whole visibility system: no private, no header files, no export
lists.
@import("std") is not special
The standard library is imported by the same mechanism; the name is resolved by
the compiler rather than by path. Package dependencies declared in
build.zig.zon work the same way, imported by their package name.
Import cycles
Two files may import each other. Zig resolves declarations lazily, so this
works as long as you do not create a genuine cycle in values: a const in A
whose initialiser needs a const in B whose initialiser needs the one in A.