# Exiting

> exit is the process stopping, not your program returning.

The snippet writes two lines and registers two `defer`s. One line comes out.

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

pub fn main(init: std.process.Init) !void {
    var buf: [1024]u8 = undefined;
    var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &buf);
    const out = &stdout_writer.interface;

    // Registered now, and every one of them is skipped below.
    defer std.debug.print("this defer never runs\n", .{});
    defer out.flush() catch {};

    try out.writeAll("first line, flushed by hand\n");
    try out.flush();

    try out.writeAll("second line, still sitting in the buffer\n");

    // The process stops here. Not a return: no defer runs, no buffer is
    // drained, and the second line above is lost with the memory holding it.
    std.process.exit(0);
}
```

*Runnable: compiled to WebAssembly and executed by CI against Zig master. (`14-os.exiting`)*

## What `exit` skips

```zig
defer std.debug.print("this defer never runs\n", .{});
defer out.flush() catch {};

try out.writeAll("second line, still sitting in the buffer\n");
std.process.exit(0);
```

`std.process.exit` is `noreturn` in the strongest sense available: it does not
return to the caller, so nothing between the call and the end of `main` ever
happens. No `defer` at any depth of the stack. No `errdefer`. No flush.

The lost line is the part worth internalising. Those bytes were in a buffer in
your process's memory, the operating system never heard about them, and the
process ended. This is the most common way output goes missing from a program
that "definitely printed it", and it is why the second `defer` above, the one
that would have flushed, is a trap: it looks like the safety net and it is not
attached to anything.

Returning from `main` normally does run every `defer`, which is why `main`
returning an error and `exit(1)` are not interchangeable. The first unwinds and
cleans up; the second stops the process where it stands.

## The status you leave

`exit` takes a `u8`. Zero means success and every other value is yours to
define, which is the same one-bit protocol the parent sees as `Term` in
[Spawning a Process](https://www.ziglang.in/learn/os/spawning/).

Returning an error from `main` sets a nonzero status for you and prints the
error name with a stack trace in debug builds. That is the right default for a
tool. Choosing a specific code is for programs whose callers branch on it,
like `grep` returning 1 for "no match" and 2 for "something went wrong", a
distinction a shell script can act on and an error message cannot.

## `cleanExit`, and why it does nothing in Debug

```zig
pub fn cleanExit(io: Io) void {
    if (builtin.mode == .debug) return;
    _ = io.lockStderr(&.{}, .no_color) catch {};
    exit(0);
}
```

A program about to exit does not need to free anything. The kernel reclaims the
whole address space, and walking a tree of allocations to release memory that
is about to vanish is work that only makes the program slower to quit.

`std.process.cleanExit` is that shortcut, with the one condition that makes it
safe to use: in Debug it returns and lets your teardown run, so the leak
detector still sees a program that freed everything and can still tell you
about the leak that matters. In release builds it exits immediately. The Zig
compiler itself calls it.

That mode dependence is why this page describes the call rather than running
it. A snippet whose output changed between `-Doptimize=Debug` and the
`ReleaseSmall` this site ships would be a snippet that passes CI and lies to
half its readers.

## `abort` is not an exit

`std.process.abort` ends the process abnormally: it raises `SIGABRT`, which
runs a handler if you installed one, and then makes sure the process dies
anyway. No status code, no cleanup, and typically a core dump.

That is the correct response to a broken invariant, where continuing would do
damage and there is nothing sensible to report. It is the wrong response to a
file that was not found. The distinction the reader should carry: `exit` is a
program finishing, `abort` is a program admitting it should not have got here.
