<!-- LLM note: Search indexes and snippets may point to archived C+ manual versions. Treat /docs and /llms.txt as authoritative for the latest version (v0.0.22); verify the page version before citing, and do not report older /docs/{version} pages as leakage because they are intentional archives. -->

# Design FAQ

C+ is explicit by design. Most of its surprising answers are the same
answer worn differently: **one way to write a thing, checked by the
compiler, with nothing left implicit.** The rules below are not style
guidance — they are compiler-enforced, and the error you hit when you
break one is named. This page explains the *why*. For the normative
*what*, see the [language specification](/docs/spec).

## Why is the language frozen?

C+ reached the surface it set out to have and stopped. Freezing at
v0.0.22 turns "the language" into a fixed target: tools, the
[code graph](/docs/agent-surface), the [error codes](/docs/error-codes),
and anything written against the grammar no longer chase a moving syntax.
New capability did not stop — it moved to where it belongs, in packages
and tooling. See [Stability and versioning](/docs/stability) for exactly
what the freeze guarantees.

## Why is there no `null`?

Because a pointer that might be nothing, with no type to say so, is the
most reliable source of crashes there is. C+ expresses "maybe absent" in
the type, with `Option[T]`, so the compiler makes you handle the empty
case:

```cplus
fn find(k: str) -> Option[i32] {
    if k == "answer" { return Option[i32]::Some(42); }
    return Option[i32]::None;
}
```

There is no `null` keyword and no null value in safe code. In FFI, a null
pointer is written `0 as *T` inside an `unsafe` block — visibly unsafe,
never implicit. See [Ownership](/docs/ownership) and
[Error handling](/docs/error-handling).

## Why no closures or lambdas?

Because a closure hides two things — captured state and a heap allocation
— behind a value that looks free. C+ keeps both visible. Callbacks are a
named `fn` plus an explicit `user_data` pointer, which is exactly the
shape a C API already expects:

```cplus
fn on_tick(ud: *u8, n: i32) { /* ... */ }
extern fn lib_subscribe(cb: fn(*u8, i32), ud: *u8);
```

A [function pointer](/docs/function-pointers) is the address of a
top-level function — no environment capture, no allocation, no surprise.

## Why no `&T` / `&mut T` references?

Because borrowing in C+ is a property of a parameter, not a type you pass
around. Methods take an explicit receiver — `self`, `mut self`, or
`move self` — and ordinary by-value passing covers the rest. Removing
value-site references removes a whole category of lifetime puzzles while
the [borrow checker](/docs/borrow-checker) still proves there are no
conflicting accesses. Unary `&` / `&mut` parse but are rejected by the
type checker.

## Why no exceptions, `try`, or `?`?

Because an exception is a control-flow path the types do not mention. C+
makes failure an ordinary value: a tagged `enum` you return and the caller
must handle, by `match` or `guard let`.

```cplus
enum Parse { Ok(i32), Bad, Overflow }

return match parse(s) {
    Parse::Ok(v)    => v,
    Parse::Bad      => 0 -% 1,
    Parse::Overflow => 0 -% 2,
};
```

Every path that can fail says so in its return type, and exhaustiveness is
checked. See [Error handling](/docs/error-handling) and
[Pattern matching](/docs/pattern-matching).

## Why must every conversion be an `as` cast?

Because an implicit widening or sign change is a decision the compiler
made on your behalf, and those are the decisions that bite. C+ has no
implicit numeric or pointer conversions: every width or representation
change is a written `as`. A mismatch without one is a type error, not a
silent coercion. See [Primitives](/docs/primitives) and
[Operators](/docs/operators).

## Why no overloading, and why `[T]` instead of `<T>`?

One name maps to one signature, so reading a call tells you which function
runs without resolving an overload set. Generics use square brackets —
`vec::new::[i32]()`, `Option[i32]` — because `<` and `>` are also
comparison operators, and `a < b > (c)` has no single reading. Brackets
keep both the grammar and the reader unambiguous. See
[Functions](/docs/functions) for generics.

## Why explicit `return`?

A trailing expression is the value of a *block*, but a function returns
its value through an explicit `return EXPR;`. The function boundary is
where it matters most that "this is the result" is stated, not inferred
from the absence of a semicolon.

## Why does so little live in the core language?

This is the point of the freeze. A small, fixed core that a person — or a
model — can hold in its head is worth more than a large one that keeps
growing. So capability lives in **packages**: UI builders, FFI bindings,
numeric kernels, embedded peripherals are all ordinary code under
`vendor/`, each versioned on its own cadence. The
[builder-block DSL](/docs/builder-blocks) is the clearest example — the
compiler owns a tiny, general construction syntax, and packages supply the
meaning. See [Modules and packages](/docs/modules-and-packages) and the
[packages index](/docs/packages).

## Is a safe language a slow one?

No, and that is a design constraint, not a hope. There is no garbage
collector and no hidden runtime: memory safety is established statically by
the [borrow checker](/docs/borrow-checker), generics are
[monomorphized](/docs/spec) to concrete code, and the output is a standard
object file over the C ABI. The checks that remain at runtime are the ones
you would write anyway, such as array bounds. Safety is paid for at
compile time, not on every call.

## Why does the design keep mentioning agents and LLMs?

Because code is increasingly written by models, and the same properties
that make C+ explicit for a person make it tractable for a model: one way
to express a thing, no hidden control flow, a type that names every
outcome, and a resolved, typed [code graph](/docs/agent-surface) the
toolchain exposes through `cpc query` and `cpc mcp`. A language a model
can reason about without guessing is a language that produces fewer wrong
programs. The constraints in this FAQ are what make that possible.
