エラーハンドリング

NagiScript のエラーハンドリング戦略を学びます。

現行仕様(v0.2.0)

Result / Option は組み込みの型としては存在しません。enum で自作して使います。エラーを上位へ伝播させる ? 演算子は未実装なので、エラー回避は if / else の分岐で行います。


Result を enum で自作する

まず enum で成功・失敗の状態を定義します。値の生成は Result.Ok のように括弧なしで生成します(データを持つバリアントのみ括弧が必要)。

result.ngs
enum Result {
    Ok
    NotFound
}

fn find(key: i32) -> Result {
    if key == 1 {
        return Result.Ok
    }
    return Result.NotFound
}

fn main() {
    val r = find(2)
    val st = match r {
        Ok => "found",
        NotFound => "missing",
    }
    println(st)
}

match のパターン

match のパターンには裸のバリアント名(Ok / NotFound)を使います。どのバリアントにも一致するワイルドカードは _ です。

strict.ngs
enum Result {
    Ok
    NotFound
}

fn find(key: i32) -> Result {
    if key == 1 {
        return Result.Ok
    }
    return Result.NotFound
}

fn main() {
    val r = find(2)
    match r {
        Ok => println("found"),
        _ => println("missing"),
    }
}

エラー回避は if / else 分岐

? 演算子は未実装なので、エラーの判定・回避は if / else と 明示的な return で行います。

guard.ngs
fn divide(a: i32, b: i32) -> i32 {
    if b == 0 {
        println("zero division")
        return 0  // エラー時は別の値で回避
    }
    return a / b
}

fn main() {
    val x = divide(10, 2)
    println(x)
}
status.ngs
enum DivResult {
    Ok
    Error
}

fn safe_divide(a: i32, b: i32) -> DivResult {
    if b == 0 {
        return DivResult.Error
    }
    return DivResult.Ok
}

fn main() {
    val r = safe_divide(10, 0)
    match r {
        Ok => println("divide ok"),
        Error => println("zero division"),
    }
}

明示 return の規約

ブロック末尾の暗黙 return(式ブロック)は非対応です。戻り値のある関数は必ず return を書き、戻り値を返さない関数は -> を付けません。

return.ngs
fn read_config(name: string) -> string {
    if name == "main" {
        return "module:main"
    }
    return "not found"
}

fn main() {
    println(read_config("main"))
}

panic

回復不能なエラーには組み込みの panic を使います。引数は 1 個で、プログラムを停止します。

panic.ngs
fn divide_or_panic(a: i32, b: i32) -> i32 {
    if b == 0 {
        panic("Division by zero")
    }
    return a / b
}

エラーハンドリングの流れ

enum で状態(Ok / NotFound / Error など)を定義
    ↓
関数の冒頭で if / else によりエラーを判定
    ↓
結果を return で明示的に返す
    ↓
match でバリアントを分岐
    ├─ Ok => 正常処理を続行
    └─ NotFound => 別の処理
    ↓
? 演算子は未実装。伝播したい場合は if / else で分岐させる