> ## Documentation Index
> Fetch the complete documentation index at: https://forst-lang.org/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Check a value

> Language-level early returns as an alternative to manual checks.

Early returns are a best practice that promotes checking required conditions as early as possible so you get bad cases out of the way.
To make this rule easy to follow, Forst has the **`ensure`** keyword. It allows you to declare what you expect to be true.

If it is not, the function returns
an error (or exits if you're inside `main`). If the condition holds, Forst keeps that knowledge for the lines
below, so you do not have to check again.

## Stop unless a value is valid

This `greet` function must not run with an empty name.

<Tabs>
  <Tab title="Forst">
    ```ft theme={"theme":{"light":"github-light-default","dark":"dark-plus"}}
    error EmptyName {}

    func greet(name String) {
    	ensure name is Min(1) else EmptyName{}
    	return "hello, " + name
    }
    ```
  </Tab>

  <Tab title="Generated Go">
    ```go theme={"theme":{"light":"github-light-default","dark":"dark-plus"}}
    // forst build
    import utf8 "unicode/utf8"

    type EmptyName struct{}

    func (e EmptyName) Error() string { return "EmptyName" }

    func (e EmptyName) ForstErrorTag() string {
    	return "main/EmptyName"
    }

    func greet(name string) (string, error) {
    	if utf8.RuneCountInString(name) < 1 {
    		return "", EmptyName{}
    	}
    	return "hello, " + name, nil
    }
    ```
  </Tab>
</Tabs>

If `name` is empty, `greet` returns `EmptyName`. `return "hello, " + name` runs
only after the check passes.

`is Min(1)` is the condition. `else EmptyName{}` is the error to return when it
fails. See [Return a named error](/docs/language/named-errors) for how to declare
errors with fields.

## Use the value after the check

After a successful check, Forst remembers that the condition held and narrows your variable.
Later lines can use `name` without repeating the bound.
[Describe a shape](/docs/language/shapes-and-constraints#reference) lists built-in
conditions such as `LessThan`, `Min`, `Present`, and `Ok`.

## Handle multiple outcomes in the same function

Use `if` when both outcomes still belong in the function. Use `ensure`
when the branch you're on only makes sense if more checks pass.

This handler ships a paid order. An unpaid order waits. A paid order must be
unlocked and must have items:

<Tabs>
  <Tab title="Forst">
    ```ft theme={"theme":{"light":"github-light-default","dark":"dark-plus"}}
    error OrderLocked { orderId: String }
    error EmptyCart { orderId: String }

    type Order = {
    	id:     String,
    	paid:   Bool,
    	locked: Bool,
    	items:  []String,
    }

    func fulfill(order Order): Result(String, Error) {
    	if order.paid {
    		ensure order.locked is False() else OrderLocked{ orderId: order.id }
    		ensure order.items is Min(1) else EmptyCart{ orderId: order.id }
    		return order.items[0]
    	}
    	return "awaiting payment"
    }
    ```
  </Tab>

  <Tab title="Generated Go">
    ```go theme={"theme":{"light":"github-light-default","dark":"dark-plus"}}
    // forst build
    type EmptyCart struct {
    	orderId string
    }

    type Order struct {
    	id     string
    	items  []string
    	locked bool
    	paid   bool
    }

    type OrderLocked struct {
    	orderId string
    }

    func (e EmptyCart) Error() string { return "error" }
    func (e OrderLocked) Error() string { return "error" }

    func (e EmptyCart) ForstErrorTag() string { return "main/EmptyCart" }
    func (e OrderLocked) ForstErrorTag() string { return "main/OrderLocked" }

    func fulfill(order Order) (string, error) {
    	if order.paid {
    		if order.locked {
    			return "", OrderLocked{orderId: order.id}
    		}
    		if len(order.items) < 1 {
    			return "", EmptyCart{orderId: order.id}
    		}
    		return order.items[0], nil
    	}
    	return "awaiting payment", nil
    }
    ```
  </Tab>
</Tabs>

If `order.paid` is false, `fulfill` returns `"awaiting payment"` and never
runs those checks. If it is paid, a locked order returns `OrderLocked` and an
empty cart returns `EmptyCart`. After both `ensure`s pass, `order.items[0]` is
safe.

But facts derived from `ensure` statements don't survive when you exit the branch. So after the `if`, Forst does not treat the order as unlocked or as having
items.

For reusable names for those rules, see
[Name a reusable rule](/docs/language/name-a-rule).

## Write a shorter check

Forst also offers shorthand forms to help with typical use cases. Take Go's `filepath.IsAbs` which returns a `bool`. You can check that result with `ensure` and
leave out `is True()`:

<Tabs>
  <Tab title="Forst">
    ```ft theme={"theme":{"light":"github-light-default","dark":"dark-plus"}}
    import "os"
    import "path/filepath"

    func openFile(path String) {
    	ensure filepath.IsAbs(path)
    	file, err := os.Open(path)
    	ensure !err
    	return file
    }
    ```
  </Tab>

  <Tab title="Generated Go">
    ```go theme={"theme":{"light":"github-light-default","dark":"dark-plus"}}
    // forst build
    import (
    	errors "errors"
    	"os"
    	"path/filepath"
    )

    func openFile(path string) (*os.File, error) {
    	if !filepath.IsAbs(path) {
    		return nil, errors.New("ensure Variable(filepath).IsAbs(Variable(path)) is Bool.True(): want true")
    	}
    	file, err := os.Open(path)
    	if err != nil {
    		return nil, err
    	}
    	return file, nil
    }
    ```
  </Tab>
</Tabs>

If the path is relative, `openFile` returns an error and never opens the file.
That keeps the open from depending on the process working directory.
`ensure filepath.IsAbs(path)` is the same as
`ensure filepath.IsAbs(path) is True()`.

The check is the bool that `filepath.IsAbs` returned. Forst does not remember
that `path` is absolute. Later lines still treat `path` as an ordinary
`String`. If you want Forst to remember that `path` is absolute, put the Go
call in a [**type guard**](/docs/language/name-a-rule) on `path`.

The same idea works for pointers, errors, and **`Result`** values. You write the
value. Forst fills in the usual check.

### Continue only when a pointer is empty

This code stores a value in `slot` only when nothing is there yet.

<Tabs>
  <Tab title="Forst">
    ```ft theme={"theme":{"light":"github-light-default","dark":"dark-plus"}}
    error SlotAlreadyTaken {}

    var slot: *Int = nil

    func claim(value Int) {
    	ensure !slot else SlotAlreadyTaken{}
    	slot = &value
    }

    func open() {
    	ensure claim(7)
    	return *slot
    }
    ```
  </Tab>

  <Tab title="Generated Go">
    ```go theme={"theme":{"light":"github-light-default","dark":"dark-plus"}}
    // forst build
    type SlotAlreadyTaken struct{}

    func (e SlotAlreadyTaken) Error() string { return "SlotAlreadyTaken" }

    func (e SlotAlreadyTaken) ForstErrorTag() string {
    	return "main/SlotAlreadyTaken"
    }

    var slot *int = nil

    func claim(value int) error {
    	if slot != nil {
    		return SlotAlreadyTaken{}
    	}
    	slot = &value
    	return nil
    }

    func open() (int, error) {
    	if err := claim(7); err != nil {
    		return 0, err
    	}
    	return *slot, nil
    }
    ```
  </Tab>
</Tabs>

`ensure !slot` is the same as `ensure slot is Nil()`. If `slot` already holds a
pointer, `claim` returns `SlotAlreadyTaken` and does not overwrite it.

`open` waits until `claim` succeeds. `ensure claim(7)` is the same as
`ensure claim(7) is Ok()`. If `claim` fails, `open` returns that same
`SlotAlreadyTaken` error. `claim` has no success value. Checking the call is
enough.

`ensure slot` (no `!`) means the pointer is non-nil, the same as
`ensure slot is Present()`. On a map or array, `Present` and `Nil` mean
nilness, not emptiness. An empty `map` or `[]` still passes `Present`. Use
`is NotEmpty()` or `is Min(n)` for length.

### Use the value from a Result

Map lookups and fallible calls return `Result`. Check success before you use
the value. After `placeOrder` returns a `Result`, this continues only on
success:

<Tabs>
  <Tab title="Forst">
    ```ft theme={"theme":{"light":"github-light-default","dark":"dark-plus"}}
    order := placeOrder({
    	stockKeepingUnit: "ITEM-1",
    	quantity:         2,
    })
    ensure order
    println(order)
    ```
  </Tab>

  <Tab title="Generated Go">
    ```go theme={"theme":{"light":"github-light-default","dark":"dark-plus"}}
    // forst build
    order, err := placeOrder(PlaceOrderInput{
    	stockKeepingUnit: "ITEM-1",
    	quantity:         2,
    })
    if err != nil {
    	return err
    }
    println(order)
    ```
  </Tab>
</Tabs>

`ensure order` is the same as `ensure order is Ok()`. On failure, the function
that contains this `ensure` returns `order`'s error (except in `main` and Go
tests). After the check, `order` is the successful value.

The same pattern works when you need that value in the next check. Here `in` is
the order request and `catalog` maps SKUs to quantities:

<Tabs>
  <Tab title="Forst">
    ```ft theme={"theme":{"light":"github-light-default","dark":"dark-plus"}}
    avail := catalog[in.stockKeepingUnit]
    ensure avail
    ensure in.quantity is Max(avail) else InsufficientStock{
    	stockKeepingUnit: in.stockKeepingUnit,
    	requested:        in.quantity,
    	available:        avail,
    }
    ```
  </Tab>

  <Tab title="Generated Go">
    ```go theme={"theme":{"light":"github-light-default","dark":"dark-plus"}}
    // forst build
    import "errors"

    var errMissingMapKey = errors.New("missing map key")

    avail, availErr := func() (int, error) {
    	v, ok := catalog[in.stockKeepingUnit]
    	if !ok {
    		return 0, errMissingMapKey
    	}
    	return v, nil
    }()
    if availErr != nil {
    	return availErr
    }
    if in.quantity > avail {
    	return InsufficientStock{
    		stockKeepingUnit: in.stockKeepingUnit,
    		requested:        in.quantity,
    		available:        avail,
    	}
    }
    ```
  </Tab>
</Tabs>

After `ensure avail`, `avail` is the quantity in the map. The next line can
compare `in.quantity` with it. If the lookup failed, the handler returns the
lookup error. If the quantity is too high, it returns `InsufficientStock` with
the SKU, the request, and what was available.

When a Go call returns `err`, write `ensure !err`. That is the same as
`ensure err is Nil()`, and a failed check returns that error. See
[Functions that can fail](/docs/language/result#handle-go-functions-with-several-returns).

### Short forms

When the type is known, bare `ensure` and `ensure !` expand to these checks.
`else` stays optional in every case.

| Type | Write | Same as |
| - | - | - |
| `Bool` | `ensure paid` | `ensure paid is True()` |
| `Bool` | `ensure !paid` | `ensure paid is False()` |
| `Result(S, F)` | `ensure order` / `ensure placeOrder(…)` | `ensure … is Ok()` |
| `*T`, `Map`, `Array` | `ensure slot` | `ensure slot is Present()` (non-nil) |
| `Error` / nilable | `ensure !err` | `ensure err is Nil()` |

Other values still need an explicit `is` condition, as `name` did in `greet`.

## Accept several valid cases

Use **`or`** to join several acceptable checks on the same value. This status
continues if it is either pending or processing:

```ft theme={"theme":{"light":"github-light-default","dark":"dark-plus"}}
error InvalidStatus {}

func advance(status String) {
	ensure status
	    is Pending()
	    or Processing()
	    else InvalidStatus{}
}
```

If both checks fail, `advance` returns `InvalidStatus`.

Write the value once. Follow it with `is Pending()`, then `or Processing()`.
Chained constraints stay together before `or`. In
`ensure password is String.Min(3).Max(32) or UUIDV4()`, the first option checks
a length range. The second runs `UUIDV4()`.

Inside a [**type guard**](/docs/language/name-a-rule), `ensure` cannot return a custom
error with `else`. Use `or` so any of several conditions can satisfy the guard.
Callers then attach the error at the call.

## Check against a named type

Write the type name without parentheses after `is` to check membership in that
type. This works for literal unions and for constrained aliases that use a
dotted chain with no `or` alternatives.

```ft theme={"theme":{"light":"github-light-default","dark":"dark-plus"}}
error InvalidStatus {}

type ActiveStatus =
    | "pending"
    | "processing"

func start(status String) {
	ensure status is ActiveStatus else InvalidStatus{}
}
```

A named constrained type lets a callee require the bound, and lets the caller
prove it once. See
[Describe a shape](/docs/language/shapes-and-constraints) for
`type HttpPort = Int.Min(1).Max(65535)` and similar aliases.

Omit parentheses for named types. Use parentheses for
guards and assertion functions (`Strong()`, `Pending()`).

For closed lists of allowed values, see
[List alternatives](/docs/language/union-and-intersection-types).

## Exit from main

`main` starts the process. A failed `ensure` there ends the process with exit
status 1. There is no caller to return an error to, so a named error after
`else` is not allowed.

Write `ensure drive(80)` when you only need that exit. Add `else { ... }` when
you want to log or clean up first. The block runs on failure. The process still
exits afterward.

This example calls `drive`, then `stop`. The first check has no block. The
second prints a line if `stop` fails.

<Tabs>
  <Tab title="Forst">
    ```ft theme={"theme":{"light":"github-light-default","dark":"dark-plus"}}
    import "fmt"

    error AlreadyStopped {}

    func stop() {
    	ensure position is GreaterThan(0) else AlreadyStopped{}
    	position = 0
    }

    func main() {
    	ensure drive(80)
    	ensure stop() else {
    		fmt.Println("failed to stop")
    	}
    }
    ```
  </Tab>

  <Tab title="Generated Go">
    ```go theme={"theme":{"light":"github-light-default","dark":"dark-plus"}}
    // forst build
    type AlreadyStopped struct{}

    func (e AlreadyStopped) Error() string { return "AlreadyStopped" }

    func stop() error {
    	if !(position > 0) {
    		return AlreadyStopped{}
    	}
    	position = 0
    	return nil
    }

    func main() {
    	if err := drive(80); err != nil {
    		fmt.Fprintf(os.Stderr, "ensure failed: %v\n", err)
    		os.Exit(1)
    	}
    	if err := stop(); err != nil {
    		fmt.Println("failed to stop")
    		fmt.Fprintf(os.Stderr, "ensure failed: %v\n", err)
    		os.Exit(1)
    	}
    }
    ```
  </Tab>
</Tabs>

If `drive` fails, the program writes to stderr and exits. `stop` never runs.
If `stop` fails, the program prints `failed to stop`, then exits. Nothing after
that `ensure` in `main` runs.

## When a later write drops the fact

A successful `ensure` establishes a fact about a variable or field. Forst
remembers that fact on later lines, so you do not re-validate before a call
that requires it.

```ft theme={"theme":{"light":"github-light-default","dark":"dark-plus"}}
error CannotShip {}

type Order = {
    address: Address,
    lastViewedAt: Int,
    amount: Int,
}

type Address = {
    country: *String,
}

is (order Order) Shippable() {
    ensure order.address.country is Present()
}

func ship(order Order.Shippable()) {}

func fulfill(order Order) {
    ensure order is Shippable() else CannotShip{}
    ship(order)
}
```

After `ensure order is Shippable() else CannotShip{}`, `ship(order)` runs
without checking `order.address.country` again. Forst already knows `order`
satisfied `Shippable()`.

### What drops the fact

A write to a field the check depends on drops the fact.

```ft theme={"theme":{"light":"github-light-default","dark":"dark-plus"}}
ensure order is Shippable() else CannotShip{}
order.address.country = nil
ship(order) // Error: order no longer satisfies Shippable
```

The write is legal. It drops `Shippable` because the country changed. Forst
reports the problem at `ship(order)` with:

* the dropped condition name (`Shippable`)
* the line where it was established
* the write that invalidated it
* how to restore it (`ensure order is Shippable()`)

Write `ensure order is Shippable()` again after changing dependent fields.

A write to an unrelated field leaves the fact intact.

```ft theme={"theme":{"light":"github-light-default","dark":"dark-plus"}}
ensure order is Shippable() else CannotShip{}
order.lastViewedAt = 1700000000
ship(order)
```

`lastViewedAt` is not part of `Shippable`, so `ship(order)` is still valid.
Reading a field never drops a fact.

Assigning `nil` to a pointer field (`order.address = nil`) drops presence facts
on `address` and its child fields. Writing through an existing pointer
(`order.address.country = "US"`) changes the pointed-to value and does not
clear presence on `address`.

## Caveats

See the [roadmap](/docs/resources/roadmap) for work that is not finished yet.

A fact survives an `if` / `else` split only when it holds on every path. Loops
assume zero iterations may run, so facts from a loop body do not apply after
the loop unless they are true even when the loop never runs.

Calling a Forst function that mutates a field the check used drops that fact.
Calls to Go packages, `unsafe`, or `reflect` drop facts on mutable arguments
the callee can reach. The call itself remains legal.

Writing through a pointer alias (`alias := &order; alias.address.country = nil`)
drops facts on the aliased value. Starting a goroutine (`go worker(order)`) or
sending data that contains pointers through a channel clears facts on shared
mutable storage.

Forst map lookups return a `Result`. Use `ensure avail` before reading the
value.

Generated Go uses ordinary conditionals and error returns for `ensure`. A
custom failure after `else` becomes a named Go error type. In `main`, a failed
check exits the process instead of returning.

## Examples

See the ensure and narrowing examples on GitHub:

* [`examples/in/ensure.ft`](https://github.com/forst-lang/forst/blob/main/examples/in/ensure.ft)
* [`examples/in/ensure_bool.ft`](https://github.com/forst-lang/forst/blob/main/examples/in/ensure_bool.ft)
* [`examples/in/ensure_else_method.ft`](https://github.com/forst-lang/forst/blob/main/examples/in/ensure_else_method.ft)
* [`examples/in/result_ensure.ft`](https://github.com/forst-lang/forst/blob/main/examples/in/result_ensure.ft)
* [`examples/in/result_void_ensure.ft`](https://github.com/forst-lang/forst/blob/main/examples/in/result_void_ensure.ft)
* [`examples/in/result_unwrap_propagate.ft`](https://github.com/forst-lang/forst/blob/main/examples/in/result_unwrap_propagate.ft)
* [`examples/in/result_if.ft`](https://github.com/forst-lang/forst/blob/main/examples/in/result_if.ft)

## Related

Continue with named errors or reusable conditions.

<CardGroup cols={2}>
  <Card title="Return a named error" icon="triangle-exclamation" href="/docs/language/named-errors">
    Give each expected failure a name and fields.
  </Card>

  <Card title="Name a reusable rule" icon="code-branch" href="/docs/language/name-a-rule">
    User-defined predicates for domain rules.
  </Card>

  <Card title="Functions that can fail" icon="circle-nodes" href="/docs/language/result">
    Result returns and Ok / Err checks.
  </Card>

  <Card title="Describe a shape" icon="table-columns" href="/docs/language/shapes-and-constraints">
    Built-in constraints on primitive fields.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.