Skip to content

Search

문서

Optionals and nil

T? and nil in Balaur Script: what nil is, where it may go, comparing with nil, or and unwrap, and how nil differs from an empty map.

이 페이지의 내용

nil means no value. Most types cannot hold it: an int is always a number and a str is always text. A value that may be missing says so in its type, with ?.

fn find(xs: [str], name: str) -> int? {
    for i in 0..xs.len() {
        if xs[i] == name {
            return i;
        }
    }
    nil
}

pub fn found(name: str) -> int {
    find(["a", "b", "c"], name).or(-1)
}

// found("b") == 1
// found("z") == -1

Where nil goes

nil is a literal, like true, and it has no type of its own. It takes the type of the place it goes to, and that place is a T?, an any or a Node.

struct Bag { best: str?, count: int? }

fn takes(v: any) -> bool { v == nil }

pub fn places() -> str {
    let a: int? = nil;
    let b: any = nil;
    let bag = Bag { best: nil, count: 3 };
    "{a == nil} {b == nil} {bag.best == nil} {takes(nil)}"
}

// places() == "true true true true"

An if with nil in one branch takes the type of the other branch:

pub fn name(k: int) -> str {
    let s = if k > 0 { "pos {k}" } else { nil };
    s.or("none")
}

// name(2) == "pos 2"
// name(0) == "none"

With nothing to say what it stands for, it does not compile. The same goes for a type that can never be nil:

pub fn bare() { let x = nil; }
pub fn never(x: int) -> bool { x == nil }

// error: 1:25: `nil` needs the type it stands for: `let x: T? = nil`
// error: 2:34: a int is never nil; only a `T?` or an `any` is

Using an optional

A T? is not a T. Arithmetic and a T's methods wait until the value is out.

Write Means
x == nil, x != nil whether there is a value
x == 3 whether it holds this value
x.or(fallback) the value, or the fallback
x.unwrap() the value, or a trap
pub fn add(x: int?) -> int { x.or(0) + 1 }
pub fn must(x: int?) -> int { x.unwrap() + 1 }
pub fn is_three(x: int?) -> bool { x == 3 }

// add(4) == 5
// add(nil) == 1
// must(4) == 5
// must(nil) traps
// is_three(3) == true

The compiler says what to do when a value might be missing:

pub fn adds(x: int?) -> int { x + 1 }
pub fn measures(x: str?) -> int { x.len() }

// error: 1:33: cannot combine int? and int
// error: 2:36: this may be nil, so it has no `len`: compare it with `nil`, or a str? has `unwrap`, `or`

A check does not change the type, so inside if x != nil a variable is still a T?. Use unwrap there, or or without the check.

pub fn checked(x: int?) -> int {
    if x != nil {
        x.unwrap() + 1
    } else {
        0
    }
}

// checked(1) == 2
// checked(nil) == 0

or takes a plain value, and to chain two optionals nest the calls: first.or(second.or(0)).

An optional field left out of a literal is nil. A class holds another of itself through an optional, which is how a linked structure is written.

pub class Link { value: int, next: Link? }

pub fn second() -> int {
    let tail = Link { value: 2 };
    let head = Link { value: 1, next: tail };
    head.next.unwrap().value
}

// second() == 2

nil is not an empty map

nil holds nothing. {} in a place that wants an any is a real map with no keys, and it can be written to. They read differently, and they mean different things to the engine: a request with a nil body sends none, and one with {} sends an empty JSON object.

pub fn kinds() -> str {
    let none: any = nil;
    let empty: any = {};
    empty["hp"] = 3;
    "{none == nil} {empty == nil} {none.kind()} {empty.kind()} {empty.len()}"
}

// kinds() == "true false nil map 1"

What is refused

T?? is refused, since one nil is enough. So is any?, because an any can hold nil already. is_some, is_none, is_nil and unwrap_or do not exist, and each error says what to write:

pub fn a(x: int?) -> bool { x.is_some() }
pub fn b(x: int?) -> bool { x.is_none() }
pub fn c(x: int?) -> int { x.unwrap_or(1) }

// error: 1:30: write `x != nil`, not `x.is_some()`
// error: 2:30: write `x == nil`, not `x.is_none()`
// error: 3:29: write `x.or(v)`, not `x.unwrap_or(v)`

Node is the one type that takes nil without a ?: a Node set to nil is no node, and node.is_valid() asks. See Nodes and components.