# `@import`

### `@import`

One of the first Zig builtins you will learn is `@import`.

You have already seen it many times:

```zig
const std = @import("std");
```

This imports Zig’s standard library and gives it the local name `std`.

Without `@import`, your program would not know where `std.debug.print` comes from.

`@import` is how Zig loads code from other files and modules.

It is one of the most important builtins in the language.

#### What `@import` Does

`@import` loads another Zig source file or builtin module.

Example:

```zig
const math = @import("math.zig");
```

This loads the file `math.zig`.

You can then access declarations inside that file:

```zig
const result = math.add(10, 20);
```

Think of `@import` as: “load this file and give me access to its contents.”

#### A Simple Example

Suppose you have this project:

```text
project/
├── main.zig
└── math.zig
```

Contents of `math.zig`:

```zig
pub fn add(a: i32, b: i32) i32 {
    return a + b;
}
```

Contents of `main.zig`:

```zig
const std = @import("std");
const math = @import("math.zig");

pub fn main() void {
    const result = math.add(5, 7);

    std.debug.print("{}\n", .{result});
}
```

Output:

```text
12
```

The file `main.zig` imports `math.zig`, then calls `math.add`.

#### `pub` Makes Declarations Visible

Notice this line:

```zig
pub fn add(a: i32, b: i32) i32 {
```

The word `pub` means public.

Only public declarations are accessible from imported files.

Example:

```zig
fn hidden() void {}
```

This function is private to its file.

If another file imports this module, it cannot access `hidden`.

But this works:

```zig
pub fn visible() void {}
```

This is an important Zig design rule: visibility is explicit.

#### Importing the Standard Library

The most common import is:

```zig
const std = @import("std");
```

`"std"` is a special builtin module provided by the compiler.

It gives access to Zig’s standard library.

For example:

```zig
std.debug.print("hello\n", .{});
```

or:

```zig
const ArrayList = std.ArrayList;
```

The standard library contains collections, allocators, file APIs, networking, JSON support, testing tools, threading primitives, and much more.

You will use `std` constantly.

#### Import Paths

`@import` takes a string path.

Example:

```zig
@import("utils.zig")
```

Relative paths are based on the importing file.

Suppose:

```text
src/
├── main.zig
└── utils/
    └── math.zig
```

From `main.zig`:

```zig
const math = @import("utils/math.zig");
```

The path uses forward slashes.

#### Imported Files Are Namespaces

When you import a file, the file becomes a namespace.

Example:

```zig
const math = @import("math.zig");
```

Then:

```zig
math.add(1, 2)
math.sub(10, 3)
math.mul(4, 5)
```

Everything stays grouped under `math`.

This avoids name conflicts.

For example, imagine two files both define `open`:

```zig
const file_api = @import("file.zig");
const network_api = @import("network.zig");
```

Then:

```zig
file_api.open(...)
network_api.open(...)
```

The namespaces keep things clear.

#### Importing Specific Declarations

Sometimes you want a shorter name.

Example:

```zig
const std = @import("std");
const print = std.debug.print;
```

Now:

```zig
print("hello\n", .{});
```

This is common in Zig code.

You still imported `std`, but you created a local shortcut.

#### `@import` Happens at Compile Time

Imports are resolved during compilation.

The compiler reads the imported files, checks them, and builds them into the final program.

There is no runtime importing system like Python’s `import` or JavaScript’s `require`.

This means:

- imports are static
- the compiler knows the full program structure
- unused code can often be removed
- errors appear during compilation

#### Zig Does Not Use Header Files

In C, you often have:

```text
math.h
math.c
```

The `.h` file contains declarations.

The `.c` file contains implementations.

Zig does not use this model.

A Zig file contains both declarations and implementations together.

Example:

```zig
pub fn add(a: i32, b: i32) i32 {
    return a + b;
}
```

No separate header file is needed.

This simplifies many projects.

#### Circular Imports

Be careful with modules importing each other.

Example:

```text
a.zig imports b.zig
b.zig imports a.zig
```

This can create dependency cycles.

Zig tries to keep compilation simple and predictable, so circular dependencies are usually a design problem.

A better design often extracts shared code into a third module.

Example:

```text
common.zig
```

Both modules import `common.zig` instead of importing each other.

#### Importing Builtin Modules

Some names are special.

Example:

```zig
const builtin = @import("builtin");
```

This gives information about the current target and compiler settings.

Example:

```zig
builtin.os.tag
builtin.cpu.arch
```

You can use this for platform-specific behavior.

Example:

```zig
if (builtin.os.tag == .windows) {
    // Windows code
} else {
    // Other systems
}
```

This is common in systems programming.

#### Zig Encourages Small Modules

A Zig project is usually split into many small files.

For example:

```text
src/
├── main.zig
├── lexer.zig
├── parser.zig
├── token.zig
├── ast.zig
└── codegen.zig
```

Each file handles one area of the program.

Then `main.zig` imports what it needs.

This style keeps projects easier to understand.

#### Importing Does Not Automatically Run Code

In some languages, importing a module can execute top-level code automatically.

Zig avoids most hidden behavior.

Top-level declarations are evaluated during compilation when needed, but Zig does not use module initialization systems like many scripting languages.

Usually, behavior starts from `main`.

This makes program startup easier to reason about.

#### A More Realistic Example

`math.zig`:

```zig
pub fn square(x: i32) i32 {
    return x * x;
}

pub fn cube(x: i32) i32 {
    return x * x * x;
}
```

`main.zig`:

```zig
const std = @import("std");
const math = @import("math.zig");

pub fn main() void {
    const a = math.square(4);
    const b = math.cube(3);

    std.debug.print("square: {}\n", .{a});
    std.debug.print("cube: {}\n", .{b});
}
```

Output:

```text
square: 16
cube: 27
```

This pattern appears constantly in Zig projects.

#### Common Beginner Mistakes

##### Forgetting `pub`

Example:

```zig
fn add(a: i32, b: i32) i32 {
    return a + b;
}
```

Then another file tries:

```zig
math.add(1, 2)
```

This fails because `add` is private.

You need:

```zig
pub fn add(a: i32, b: i32) i32 {
```

##### Wrong Relative Path

Example:

```zig
@import("math.zig")
```

If the file is actually inside `utils/`, the path is wrong.

Correct:

```zig
@import("utils/math.zig")
```

##### Forgetting the Namespace

Example:

```zig
const math = @import("math.zig");

add(1, 2)
```

This fails because `add` belongs to the `math` namespace.

Correct:

```zig
math.add(1, 2)
```

#### Key Idea

`@import` is Zig’s mechanism for loading modules and source files.

Imported files become namespaces.

Public declarations are exposed with `pub`.

Imports are resolved at compile time, not runtime.

When you see:

```zig
const something = @import("file.zig");
```

read it as:

“Load this Zig module and make its public declarations available through the name `something`.”

