# Sentinel Termination

> Sequences that end in a known marker.

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

test "sentinel-terminated array" {
    // `[3:0]u8` is three bytes plus a guaranteed 0 at index 3.
    const array: [3:0]u8 = .{ 1, 2, 3 };
    try expect(array.len == 3); // the sentinel is not counted
    try expect(array[3] == 0); // but it is there
}

test "string literals are sentinel terminated" {
    const literal = "hi";
    try expect(@TypeOf(literal) == *const [2:0]u8);
    try expect(literal.len == 2);
    try expect(literal[2] == 0); // free C compatibility
}

test "span recovers the length from a sentinel" {
    const c_string: [*:0]const u8 = "hello";
    // `[*:0]T` has no length, but the sentinel makes it recoverable.
    const slice = std.mem.span(c_string);
    try expect(slice.len == 5);
    try expect(std.mem.eql(u8, slice, "hello"));
}

test "sentinel slices coerce to ordinary ones" {
    const sentinel_slice: [:0]const u8 = "abc";
    const plain: []const u8 = sentinel_slice;
    try expect(plain.len == 3);
}
```

*Runnable: compiled to WebAssembly and executed by CI against Zig master. (`02-language.sentinel-termination`)*

A sentinel-terminated type guarantees a specific value sits just past the end.
The syntax puts it after a colon:

| Type | Meaning |
| --- | --- |
| `[3:0]u8` | 3 bytes, plus a guaranteed `0` at index 3 |
| `[:0]u8` | slice with a `0` after the last element |
| `[*:0]u8` | many-item pointer, terminated by `0` |

The sentinel is **not** counted in `.len`, but it is really there and you may
read it.

## String literals get this for free

```zig
const literal = "hi";   // *const [2:0]u8
literal.len;            // 2
literal[2];             // 0
```

So passing a Zig string literal to a C function that wants `const char *` needs
no conversion and no copy. That is the whole point: C compatibility without
paying for it in ordinary Zig code, where you still get the length.

## Recovering a length

`[*:0]T` has no length, but the sentinel makes it discoverable:

```zig
const slice = std.mem.span(c_string);   // scans to the sentinel
```

This is O(n), which is exactly the cost C pays for `strlen`. Do it once at the
boundary and work with slices from there.
