# Small Values Are Fine to Copy

### Avoiding Copies

Copying data is sometimes necessary, but unnecessary copying is one of the easiest ways to waste time and memory.

A copy takes bytes from one place and writes them to another place. For small values, this is cheap. For large arrays, large structs, strings, buffers, and file contents, copying can become expensive.

In Zig, you should learn to notice when data is copied and when data is only referenced.

## Small Values Are Fine to Copy

Small integers, floats, booleans, enums, and small structs are usually fine to pass by value.

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

This copies two `i32` values into the function. That is normal and cheap.

A small struct is also usually fine:

```zig
const Point = struct {
    x: f32,
    y: f32,
};

fn lengthSquared(p: Point) f32 {
    return p.x * p.x + p.y * p.y;
}
```

The struct has only two `f32` fields. Passing it by value is reasonable.

## Large Values Should Usually Be Borrowed

A large array should not usually be passed by value.

```zig
fn process(data: [4096]u8) void {
    _ = data;
}
```

This copies the whole array into the function.

A better version uses a slice:

```zig
fn process(data: []const u8) void {
    _ = data;
}
```

A slice does not copy the array contents. It only stores a pointer and a length.

That means this call is cheap:

```zig
var buffer: [4096]u8 = undefined;
process(buffer[0..]);
```

The function can read the original memory without copying it.

## Slices Are Views

A slice is a view into existing memory.

```zig
const text = "hello zig";
const part = text[0..5];
```

`part` does not contain a new copy of `"hello"`.

It points into the same memory as `text`.

```text
text: hello zig
part: hello
```

This is useful for parsing.

Instead of copying every word, token, or field, you can store slices into the original input.

```zig
const Token = struct {
    text: []const u8,
};
```

A token can refer to part of the source text.

That avoids thousands or millions of small copies.

## Use `[]const T` for Read-Only Borrowing

When a function only needs to read data, use a const slice:

```zig
fn countSpaces(text: []const u8) usize {
    var count: usize = 0;

    for (text) |ch| {
        if (ch == ' ') {
            count += 1;
        }
    }

    return count;
}
```

This communicates two things:

The function does not own the memory.

The function will not modify the memory.

That is an efficient and clear API.

## Use `[]T` for Mutable Borrowing

When a function needs to modify data, use a mutable slice:

```zig
fn fillZeroes(buffer: []u8) void {
    for (buffer) |*byte| {
        byte.* = 0;
    }
}
```

The function still does not own the memory. It only receives permission to change it.

```zig
var buffer: [128]u8 = undefined;
fillZeroes(buffer[0..]);
```

No heap allocation is needed. No large copy is needed.

## Copying Strings

In Zig, strings are usually byte slices.

A string literal has type compatible with `[]const u8`.

```zig
const name = "zig";
```

If you only need to inspect the string, borrow it:

```zig
fn printName(name: []const u8) void {
    std.debug.print("{s}\n", .{name});
}
```

If you need to keep the string after the original memory may disappear, then you need a copy.

```zig
const owned_name = try allocator.dupe(u8, name);
defer allocator.free(owned_name);
```

This is the core rule:

Borrow when the original memory remains valid.

Copy when you need independent ownership.

## Ownership Decides Whether to Copy

A copy is often an ownership decision.

Suppose a function receives input:

```zig
fn parseLine(line: []const u8) void {
    _ = line;
}
```

This function borrows `line`. It must not store the slice somewhere that outlives the original memory.

Now compare:

```zig
const Record = struct {
    name: []u8,
};

fn makeRecord(allocator: std.mem.Allocator, name: []const u8) !Record {
    return .{
        .name = try allocator.dupe(u8, name),
    };
}
```

This function copies `name`, because the returned `Record` owns its own memory.

That copy is necessary if the original `name` may disappear.

## Beware Hidden Copies Through Arrays

Arrays in Zig are values.

That means assigning an array copies the whole array.

```zig
var a = [_]u8{ 1, 2, 3, 4 };
var b = a;
```

Now `b` is a separate array.

Changing `b` does not change `a`.

```zig
b[0] = 99;
```

`a[0]` is still `1`.

This is useful, but it can surprise beginners when the array is large.

For large data, prefer slices or pointers.

## Struct Copies

Structs are also values.

```zig
const Big = struct {
    data: [4096]u8,
};

fn process(x: Big) void {
    _ = x;
}
```

Calling `process(big)` copies the whole struct.

A better version may use a pointer:

```zig
fn process(x: *const Big) void {
    _ = x;
}
```

Then call:

```zig
process(&big);
```

Now the function receives the address of `big`.

No full struct copy is needed.

## Pointer vs Slice

Use a pointer when you refer to one object:

```zig
fn updateUser(user: *User) void {
    user.score += 1;
}
```

Use a slice when you refer to many items:

```zig
fn updateUsers(users: []User) void {
    for (users) |*user| {
        user.score += 1;
    }
}
```

Both avoid copying the full data.

The difference is shape:

A pointer means one object.

A slice means many contiguous objects.

## Returning Large Data

Returning a small value is fine:

```zig
fn makePoint() Point {
    return .{ .x = 1, .y = 2 };
}
```

Returning a large array may copy a lot of data:

```zig
fn makeBuffer() [4096]u8 {
    var buffer: [4096]u8 = undefined;
    return buffer;
}
```

The compiler may optimize some copies, but API design should still be clear.

For large results, consider caller-provided storage:

```zig
fn makeBuffer(out: []u8) void {
    for (out) |*byte| {
        byte.* = 0;
    }
}
```

The caller owns the memory.

```zig
var buffer: [4096]u8 = undefined;
makeBuffer(buffer[0..]);
```

No heap allocation is needed. No ownership confusion is introduced.

## In-Place Modification

Copying is often avoidable when you modify data in place.

Copying version:

```zig
fn uppercaseCopy(allocator: std.mem.Allocator, input: []const u8) ![]u8 {
    const out = try allocator.dupe(u8, input);

    for (out) |*ch| {
        if (ch.* >= 'a' and ch.* <= 'z') {
            ch.* -= 32;
        }
    }

    return out;
}
```

In-place version:

```zig
fn uppercaseInPlace(buffer: []u8) void {
    for (buffer) |*ch| {
        if (ch.* >= 'a' and ch.* <= 'z') {
            ch.* -= 32;
        }
    }
}
```

The in-place version is faster and allocates nothing, but it changes the input.

That tradeoff must be clear in the API name and type.

## `const` Helps API Design

`const` helps show whether copying is needed.

Read-only input:

```zig
fn hash(data: []const u8) u64 {
    _ = data;
    return 0;
}
```

Mutable input:

```zig
fn normalize(data: []u8) void {
    _ = data;
}
```

Owned output:

```zig
fn clone(allocator: std.mem.Allocator, data: []const u8) ![]u8 {
    return try allocator.dupe(u8, data);
}
```

These signatures tell the reader what happens to memory.

## Avoid Copying in Parsers

Parsers often process large input.

Bad parser design copies each piece:

```zig
const Field = struct {
    value: []u8,
};
```

Every field needs allocation and copying.

Better parser design stores views:

```zig
const Field = struct {
    value: []const u8,
};
```

Each field points into the original input.

This is much faster, but the original input must remain alive while the parsed fields are used.

That lifetime rule is essential.

## Avoid Copying in File Processing

Suppose you read a whole file into memory:

```zig
const data = try file.readToEndAlloc(allocator, max_size);
defer allocator.free(data);
```

After this, prefer slicing the buffer.

```zig
const header = data[0..16];
const body = data[16..];
```

Do not copy the header and body unless you need independent ownership.

The file buffer already contains the bytes.

## Avoid Copying in Collections

When adding data to a collection, decide whether the collection owns the data.

Borrowing collection:

```zig
const Entry = struct {
    name: []const u8,
};
```

Owning collection:

```zig
const Entry = struct {
    name: []u8,
};
```

The owning version usually requires copying:

```zig
entry.name = try allocator.dupe(u8, input_name);
```

The borrowing version avoids copying but depends on external lifetime.

Neither is always correct. Choose based on ownership.

## `std.mem.copyForwards`

When you do need to copy memory, Zig provides standard library functions.

```zig
std.mem.copyForwards(u8, dst, src);
```

This copies from `src` into `dst`.

Example:

```zig
var dst: [5]u8 = undefined;
const src = "hello";

std.mem.copyForwards(u8, dst[0..], src);
```

Use library functions instead of writing manual byte-copy loops.

They are clearer and may be optimized well.

## Do Not Fear Necessary Copies

Avoiding copies does not mean “never copy.”

A copy is correct when:

- you need ownership
- you need to mutate without changing the original
- you need stable data after input memory disappears
- you need compact storage
- you need to send data to another subsystem

The goal is to remove accidental copies, not useful ones.

## Mental Model

When you see data move through a Zig program, ask:

- Is this value small or large?
- Is this an array copy?
- Is this a struct copy?
- Could a slice avoid copying?
- Could a pointer avoid copying?
- Who owns this memory?
- How long does the original memory live?
- Does this function need to mutate the data?
- Does this result need independent ownership?

In Zig, performance and ownership are connected.

Borrow with slices and pointers when possible.

Copy when ownership requires it.

