# Slices in Detail

### Slices in Detail

A slice is a view into a sequence of values.

It does not own the values. It points to values stored somewhere else.

```zig
const values = [_]i32{ 10, 20, 30, 40 };

const part = values[1..3];
```

The slice `part` refers to this part of the array:

```text
20, 30
```

It does not copy those values. It only describes where they are and how many there are.

#### What a Slice Contains

A slice contains two pieces of information:

```text
pointer to the first element
length
```

So this type:

```zig
[]const i32
```

means:

```text
a slice of constant i32 values
```

This type:

```zig
[]i32
```

means:

```text
a slice of mutable i32 values
```

The difference matters.

A mutable slice lets you change the values through the slice. A constant slice lets you read the values, but not change them.

#### Creating a Slice from an Array

Use range syntax:

```zig
const values = [_]i32{ 10, 20, 30, 40 };

const all = values[0..];
const first_two = values[0..2];
const middle = values[1..3];
```

The ranges mean:

| Expression | Values |
|---|---|
| `values[0..]` | `10, 20, 30, 40` |
| `values[0..2]` | `10, 20` |
| `values[1..3]` | `20, 30` |

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

So:

```zig
values[1..3]
```

means:

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

That gives indexes `1` and `2`.

#### Slice Length

A slice has a `.len` field.

```zig
const values = [_]i32{ 10, 20, 30, 40 };
const part = values[1..3];

const n = part.len;
```

Here, `part.len` is `2`.

You can loop over a slice the same way you loop over an array:

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

pub fn main() void {
    const values = [_]i32{ 10, 20, 30, 40 };
    const part = values[1..3];

    for (part) |value| {
        std.debug.print("{}\n", .{value});
    }
}
```

Output:

```text
20
30
```

#### Slices Do Not Own Memory

This is the most important rule.

A slice refers to memory owned by something else.

```zig
var values = [_]i32{ 10, 20, 30, 40 };
var part = values[1..3];

part[0] = 99;
```

Now `values` contains:

```text
10, 99, 30, 40
```

The slice did not contain a separate copy. It pointed into the original array.

This is why slices are useful. You can pass part of an array to a function without copying it.

#### Passing Slices to Functions

A function that accepts a slice can work with many lengths.

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

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

    return total;
}
```

This function accepts any number of `i32` values.

```zig
const a = [_]i32{ 1, 2, 3 };
const b = [_]i32{ 10, 20, 30, 40, 50 };

const x = sum(a[0..]);
const y = sum(b[1..4]);
```

The first call passes all of `a`.

The second call passes:

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

This is more flexible than a function that accepts a fixed array:

```zig
fn sumThree(values: [3]i32) i32 {
    return values[0] + values[1] + values[2];
}
```

`sumThree` accepts exactly 3 values. `sum` accepts any length.

#### Constant Slices

A constant slice prevents mutation through the slice.

```zig
fn printAll(values: []const i32) void {
    for (values) |value| {
        std.debug.print("{}\n", .{value});
    }
}
```

The function can read `values`, but it cannot do this:

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

Use `[]const T` when a function only needs to read the data.

This is a good default for function parameters.

```zig
fn contains(values: []const i32, target: i32) bool {
    for (values) |value| {
        if (value == target) return true;
    }

    return false;
}
```

The function does not need to modify the slice, so `[]const i32` is the right type.

#### Mutable Slices

Use `[]T` when a function needs to modify values.

```zig
fn fill(values: []i32, replacement: i32) void {
    for (values) |*value| {
        value.* = replacement;
    }
}
```

Usage:

```zig
var values = [_]i32{ 1, 2, 3, 4 };

fill(values[0..], 0);
```

Now the array contains:

```text
0, 0, 0, 0
```

The loop uses:

```zig
|*value|
```

This captures each element by pointer, so the function can write to it.

Then:

```zig
value.* = replacement;
```

writes into the element.

#### Slicing a Slice

You can create a smaller slice from an existing slice.

```zig
const values = [_]i32{ 10, 20, 30, 40, 50 };

const a = values[0..];
const b = a[1..4];
const c = b[1..];
```

The values are:

| Name | Values |
|---|---|
| `a` | `10, 20, 30, 40, 50` |
| `b` | `20, 30, 40` |
| `c` | `30, 40` |

All of these slices refer to the same original array.

No values are copied.

#### Bounds Checking

Zig checks slice indexes in safe build modes.

```zig
const values = [_]i32{ 10, 20, 30 };

const bad = values[1..5];
```

This is invalid because index `5` is past the end of the array.

For a slice of length `3`, valid indexes are:

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

For slicing ranges, the end may equal the length.

```zig
const ok = values[1..3];
```

This is valid because it stops before index `3`.

But this is invalid:

```zig
const bad = values[1..4];
```

Index `4` is beyond the end.

#### Empty Slices

A slice can be empty.

```zig
const values = [_]i32{ 10, 20, 30 };

const empty = values[1..1];
```

The start and end are the same, so the slice has length zero.

```zig
empty.len == 0
```

An empty slice is valid. It simply has no elements.

This is useful because many functions can handle empty input naturally.

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

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

    return total;
}
```

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

#### Slice Syntax Review

Here are the common forms:

| Syntax | Meaning |
|---|---|
| `array[0..]` | Slice from index 0 to the end |
| `array[start..end]` | Slice from `start` up to but not including `end` |
| `slice[start..end]` | Smaller slice from an existing slice |
| `array[index]` | One element, not a slice |

Do not confuse these:

```zig
values[1]
```

This gives one value.

```zig
values[1..2]
```

This gives a slice containing one value.

The types are different.

If `values` is an array of `i32`, then:

```zig
values[1]
```

has type:

```zig
i32
```

But:

```zig
values[1..2]
```

has type:

```zig
[]const i32
```

or:

```zig
[]i32
```

depending on whether the original data is constant or mutable.

#### Slices and Strings

In Zig, strings are commonly represented as slices of bytes.

```zig
[]const u8
```

means:

```text
read-only sequence of bytes
```

Many functions that accept text use `[]const u8`.

Example:

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

fn greet(name: []const u8) void {
    std.debug.print("Hello, {s}!\n", .{name});
}

pub fn main() void {
    greet("Zig");
}
```

Output:

```text
Hello, Zig!
```

The formatting marker `{s}` prints a byte slice as a string.

A string literal can be passed where `[]const u8` is expected.

#### Slices and Lifetime

A slice must not outlive the memory it points to.

This is invalid in spirit:

```zig
fn bad() []const i32 {
    const values = [_]i32{ 1, 2, 3 };
    return values[0..];
}
```

The array `values` lives inside the function. When the function returns, that local array is gone. Returning a slice to it would leave the caller with a pointer to invalid memory.

Zig tries to catch many lifetime mistakes, but you should learn the rule early:

A slice is only valid while the original storage is valid.

Good examples of valid storage:

```text
global constant data
caller-owned arrays
heap allocations that are still alive
buffers that remain in scope
```

Bad examples:

```text
local arrays that disappear after return
temporary buffers that get freed
memory owned by an allocator after deallocation
```

#### Slices and Allocation

A slice itself does not allocate memory.

This function does not allocate:

```zig
fn firstHalf(values: []const i32) []const i32 {
    return values[0 .. values.len / 2];
}
```

It returns a view into the original slice.

This is cheap. It only creates another pointer and length pair.

Allocation happens only when you explicitly ask an allocator for memory.

```zig
const buffer = try allocator.alloc(u8, 1024);
```

That returns a slice of newly allocated memory:

```zig
[]u8
```

The allocator owns the memory until you free it.

```zig
defer allocator.free(buffer);
```

Now the slice `buffer` points to heap memory.

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

Do not do this:

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

The array disappears when the function returns.

Instead, use caller-provided memory, allocated memory, or constant data.

Constant data example:

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

Caller-provided buffer example:

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

#### Common Mistake: Expecting a Copy

This code modifies the original array:

```zig
var values = [_]i32{ 1, 2, 3, 4 };
var part = values[1..3];

part[0] = 99;
```

Afterward:

```text
values = 1, 99, 3, 4
```

A slice is a view, not a copy.

If you need a separate copy, allocate or create another array and copy the data.

#### Common Mistake: Using Mutable Slices When Read-Only Is Enough

This function should use `[]const i32`:

```zig
fn printAll(values: []i32) void {
    for (values) |value| {
        std.debug.print("{}\n", .{value});
    }
}
```

It does not modify the slice, so write:

```zig
fn printAll(values: []const i32) void {
    for (values) |value| {
        std.debug.print("{}\n", .{value});
    }
}
```

This makes the function easier to call and safer to use.

#### A Complete Example

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

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

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

    return total;
}

fn fill(values: []i32, replacement: i32) void {
    for (values) |*value| {
        value.* = replacement;
    }
}

pub fn main() void {
    var numbers = [_]i32{ 10, 20, 30, 40, 50 };

    const middle = numbers[1..4];
    std.debug.print("middle sum = {}\n", .{sum(middle)});

    fill(numbers[0..2], 0);

    for (numbers) |value| {
        std.debug.print("{} ", .{value});
    }
    std.debug.print("\n", .{});
}
```

Output:

```text
middle sum = 90
0 0 30 40 50
```

The slice `middle` refers to `20, 30, 40`.

The call:

```zig
fill(numbers[0..2], 0);
```

modifies the first two elements of the original array.

#### Summary

A slice is a pointer plus a length.

It is written like this:

```zig
[]T
```

or:

```zig
[]const T
```

Use `[]const T` when you only need to read values. Use `[]T` when you need to modify values.

A slice does not own memory. It points to memory owned by an array, an allocation, a string literal, or some other storage.

Slices are one of the most important types in Zig. They let functions work with data of many lengths without copying the data.

