# Slices

### Slices

A slice is a view into a sequence of values.

A slice does not own the values. It points to existing memory and stores how many items are available.

The type of a slice looks like this:

```zig
[]T
```

Read it as:

```text
slice of T
```

For example:

```zig
[]u8
```

means:

```text
slice of u8 values
```

And:

```zig
[]const u8
```

means:

```text
slice of read-only u8 values
```

Slices are one of the most common types in Zig. They are used for arrays, buffers, strings, file contents, network data, and many standard library APIs.

#### A Slice Is Pointer Plus Length

A slice contains two pieces of information:

| Part | Meaning |
|---|---|
| pointer | address of the first item |
| length | number of items |

This is why slices are safer than many item pointers.

A many item pointer says:

```zig
[*]u8
```

That means “there are some `u8` values starting here,” but it does not say how many.

A slice says:

```zig
[]u8
```

That means “there are `u8` values starting here, and this slice knows how many.”

Because the slice has a length, Zig can check indexes in safe build modes.

#### Creating a Slice from an Array

You can create a slice from an array using range syntax:

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

pub fn main() void {
    var data = [_]u8{ 10, 20, 30, 40 };

    const slice = data[0..];

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

Output:

```text
{ 10, 20, 30, 40 }
```

The expression:

```zig
data[0..]
```

means:

```text
from index 0 to the end
```

So `slice` views the whole array.

You can also slice part of the array:

```zig
const middle = data[1..3];
```

This means:

```text
start at index 1, stop before index 3
```

So it contains:

```text
20, 30
```

The start index is included. The end index is excluded.

#### Slice Ranges Are Half-Open

Zig slice ranges use half-open intervals:

```zig
start..end
```

This includes `start`, but excludes `end`.

Example:

```zig
var data = [_]u8{ 10, 20, 30, 40 };

const a = data[0..2]; // 10, 20
const b = data[2..4]; // 30, 40
```

This is useful because the length is easy to calculate:

```text
end - start
```

So:

```zig
data[1..3]
```

has length:

```text
3 - 1 = 2
```

It contains two values:

```text
data[1], data[2]
```

#### Reading from a Slice

You can read values from a slice using indexes:

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

pub fn main() void {
    var data = [_]u8{ 10, 20, 30, 40 };
    const slice = data[1..3];

    std.debug.print("{}\n", .{slice[0]});
    std.debug.print("{}\n", .{slice[1]});
}
```

Output:

```text
20
30
```

The slice starts at `data[1]`, but the slice’s own indexes start at `0`.

So:

```text
slice[0] is data[1]
slice[1] is data[2]
```

This is important. A slice has its own view of the memory.

#### Writing Through a Mutable Slice

If the slice is mutable, you can change the underlying memory through it.

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

pub fn main() void {
    var data = [_]u8{ 10, 20, 30, 40 };

    const slice = data[1..3];
    slice[0] = 99;

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

Output:

```text
{ 10, 99, 30, 40 }
```

The slice did not copy the array. It pointed into the same memory.

This line:

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

changed `data[1]`.

That is the key rule:

Changing a slice changes the memory it views.

#### Const Slices

A const slice lets you read values, but not modify them.

```zig
const data = [_]u8{ 10, 20, 30, 40 };
const slice: []const u8 = data[0..];
```

The type:

```zig
[]const u8
```

means:

```text
slice of constant u8 values
```

You can read:

```zig
const x = slice[0];
```

But you cannot write:

```zig
// slice[0] = 99; // error
```

This is the standard way to pass read-only buffers to functions.

Example:

```zig
fn printBytes(bytes: []const u8) void {
    for (bytes) |byte| {
        _ = byte;
    }
}
```

The function promises not to modify the bytes.

#### Mutable Slice vs Const Slice

These two types are different:

```zig
[]u8
[]const u8
```

A `[]u8` slice allows mutation.

A `[]const u8` slice only allows reading.

A mutable slice can be passed where a const slice is expected:

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

pub fn main() void {
    var data = [_]u8{ 1, 2, 3 };
    const mutable_slice: []u8 = data[0..];

    readOnly(mutable_slice);
}
```

This is allowed because a function that only reads is safe to call with mutable data.

But the reverse is not allowed. You cannot pass read-only data to a function that may modify it.

```zig
fn modify(bytes: []u8) void {
    bytes[0] = 99;
}

pub fn main() void {
    const data = [_]u8{ 1, 2, 3 };
    const readonly_slice: []const u8 = data[0..];

    // modify(readonly_slice); // error
}
```

This protects data that should not be changed.

#### Slice Length

Every slice has a `.len` field.

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

pub fn main() void {
    var data = [_]u8{ 10, 20, 30, 40 };
    const slice = data[1..3];

    std.debug.print("len = {}\n", .{slice.len});
}
```

Output:

```text
len = 2
```

The length is the number of items, not the number of bytes.

For `[]u8`, the number of items and number of bytes are the same.

For `[]u32`, they are different:

```zig
var numbers = [_]u32{ 1, 2, 3 };
const slice = numbers[0..];

std.debug.print("{}\n", .{slice.len}); // 3
```

The slice length is `3`, because there are 3 `u32` values.

The memory size is larger because each `u32` uses 4 bytes.

#### Iterating Over a Slice

Use `for` to loop over a slice:

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

pub fn main() void {
    const data = [_]u8{ 10, 20, 30 };

    for (data[0..]) |value| {
        std.debug.print("{}\n", .{value});
    }
}
```

Output:

```text
10
20
30
```

Here, `value` is a copy of each item.

If you want to modify each item, capture a pointer:

```zig
pub fn main() void {
    var data = [_]u8{ 10, 20, 30 };

    for (data[0..]) |*value| {
        value.* += 1;
    }
}
```

After the loop, `data` contains:

```text
11, 21, 31
```

The `|*value|` syntax means each loop variable is a pointer to the current item.

Then:

```zig
value.*
```

accesses the actual item.

#### Passing Slices to Functions

Slices are the normal way to pass a sequence to a function.

```zig
fn sum(values: []const i32) i32 {
    var total: i32 = 0;

    for (values) |value| {
        total += value;
    }

    return total;
}
```

Use it like this:

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

fn sum(values: []const i32) i32 {
    var total: i32 = 0;

    for (values) |value| {
        total += value;
    }

    return total;
}

pub fn main() void {
    const numbers = [_]i32{ 1, 2, 3, 4 };

    const result = sum(numbers[0..]);

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

Output:

```text
sum = 10
```

The function does not care whether the values came from a fixed array, heap allocation, or part of another buffer. It only needs a slice.

This is one reason slices are so useful. They separate “how memory was created” from “how memory is used.”

#### Returning Slices

A function can return a slice, but the memory behind the slice must still be valid.

This is wrong:

```zig
fn badSlice() []u8 {
    var data = [_]u8{ 1, 2, 3 };
    return data[0..];
}
```

The array `data` is local to `badSlice`. When the function returns, `data` is gone. The returned slice would point to invalid memory.

Instead, the caller can provide the memory:

```zig
fn firstTwo(buffer: []u8) []u8 {
    return buffer[0..2];
}
```

Use it like this:

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

fn firstTwo(buffer: []u8) []u8 {
    return buffer[0..2];
}

pub fn main() void {
    var data = [_]u8{ 10, 20, 30, 40 };

    const result = firstTwo(data[0..]);

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

Output:

```text
{ 10, 20 }
```

This is valid because `data` lives in `main`, and the returned slice still points into valid memory.

The rule is:

A slice must not outlive the memory it points to.

#### Empty Slices

A slice can be empty.

```zig
const empty = data[0..0];
```

Its length is `0`.

An empty slice still has a type:

```zig
[]u8
```

or:

```zig
[]const u8
```

You can pass empty slices to functions safely, as long as the function handles length `0`.

Example:

```zig
fn sum(values: []const i32) i32 {
    var total: i32 = 0;

    for (values) |value| {
        total += value;
    }

    return total;
}
```

If `values.len` is `0`, the loop runs zero times, and the result is `0`.

#### Slicing a Slice

You can create a smaller slice from an existing slice.

```zig
var data = [_]u8{ 10, 20, 30, 40, 50 };
const all = data[0..];

const middle = all[1..4];
```

`middle` contains:

```text
20, 30, 40
```

Again, this does not copy the data. It creates another view into the same memory.

You can keep slicing:

```zig
const smaller = middle[1..2];
```

`smaller` contains:

```text
30
```

All of these slices still refer to the original array.

#### Slices and Strings

In Zig, strings are usually byte slices.

A string literal has a type related to an array of bytes, and it can be used as:

```zig
[]const u8
```

Example:

```zig
const message: []const u8 = "hello";
```

This means:

```text
message is a read-only slice of bytes
```

The string `"hello"` contains bytes for the characters:

```text
h e l l o
```

So:

```zig
message.len
```

is:

```text
5
```

Zig strings are bytes. They are often UTF-8 text, but Zig does not hide that from you. This means string processing is also byte processing unless you explicitly handle Unicode code points.

#### Slice Bounds

A slice knows its length, so Zig can check whether an index is valid in safe modes.

This is valid:

```zig
const x = slice[0];
```

only if:

```text
slice.len > 0
```

This is invalid if the slice has length 3:

```zig
const x = slice[3];
```

The valid indexes are:

```text
0, 1, 2
```

Index `3` is one past the end.

The same applies to slicing:

```zig
const part = slice[1..3];
```

This is valid only if `slice.len >= 3`.

Bad indexes are bugs. Zig tries to catch them early.

#### Common Pattern: Buffer In, Slice Out

Many Zig functions receive a buffer and return the part that was actually used.

Example:

```zig
fn writeHello(buffer: []u8) []u8 {
    buffer[0] = 'h';
    buffer[1] = 'i';

    return buffer[0..2];
}
```

Use it like this:

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

fn writeHello(buffer: []u8) []u8 {
    buffer[0] = 'h';
    buffer[1] = 'i';

    return buffer[0..2];
}

pub fn main() void {
    var storage: [16]u8 = undefined;

    const used = writeHello(storage[0..]);

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

Output:

```text
hi
```

The function receives storage from the caller. It writes into it. Then it returns a slice showing which part contains useful data.

This pattern avoids heap allocation.

#### Common Mistake: Thinking a Slice Owns Memory

A slice does not own memory.

This code:

```zig
const part = data[1..3];
```

does not create a new array.

It creates a view into `data`.

If `data` changes, the slice sees the change.

If `data` becomes invalid, the slice becomes invalid too.

This is the most important thing to remember about slices.

#### Common Mistake: Returning a Slice to Local Data

This is invalid design:

```zig
fn makeName() []const u8 {
    const name = [_]u8{ 'z', 'i', 'g' };
    return name[0..];
}
```

The slice points into `name`, but `name` dies when the function returns.

Instead, use one of these patterns:

Return a string literal:

```zig
fn name() []const u8 {
    return "zig";
}
```

String literals have static storage, so this is fine.

Or let the caller provide storage:

```zig
fn writeName(buffer: []u8) []u8 {
    buffer[0] = 'z';
    buffer[1] = 'i';
    buffer[2] = 'g';
    return buffer[0..3];
}
```

Or allocate memory and make ownership clear:

```zig
fn makeName(allocator: std.mem.Allocator) ![]u8 {
    const result = try allocator.alloc(u8, 3);
    result[0] = 'z';
    result[1] = 'i';
    result[2] = 'g';
    return result;
}
```

In the allocation version, the caller must later free the returned slice.

#### The Main Idea

A slice is a pointer plus a length.

It views memory that exists somewhere else.

Use slices for buffers, arrays, strings, and function parameters that operate on sequences.

Prefer slices over many item pointers in normal Zig code, because slices carry their length and are easier to use safely.

The central rule is simple:

A slice is only valid while the memory behind it is valid.

