エラーハンドリング
LuneScript では、nil を利用したシンプルなエラー表現から、型安全な __Ret
型、そしてそれらを効率的に扱う「エラーの委譲」機能まで、堅牢なアプリケーションを開発するための仕組みが整っています。
__Ret<T, E> 型
__Ret<T, E> は、処理が成功した際の値(Ok)と、失敗した際のエラー情報(Err)を一つの型で表現するための代数的データ型 (ADT) です。Rust の
Result<T, E> に相当します。
// 内部的な定義イメージ
alge __Ret<T, E> {
Ok( val: T ),
Err( err: E ),
}
基本的な使い方
値を返すときは .Ok(値)、エラーを返すときは .Err(エラー) を使用します。受け取った側は match 文で安全に分解できます。
fn divide( a: real, b: real ): __Ret<real, str> {
if b == 0.0 {
return .Err( "division by zero" );
}
return .Ok( a / b );
}
let result = divide( 10.0, 0.0 );
match result {
case .Ok( val ) { print( "Success:", val ); }
case .Err( msg ) { print( "Error:", msg ); }
}
__Er 型
エラー情報として、単なる文字列ではなく位置情報などを含む詳細な情報が必要な場合、標準の __Er 型を利用することが推奨されます。
fn sub(): __Ret<int, __Er> {
return .Err( __serr( "something went wrong" ) );
}
エラーの委譲 (! 演算子)
エラーが発生する可能性のある関数を連続して呼び出す場合、都度 match や if でチェックするとロジックが見づらくなります。!
演算子はこのチェックを自動化します。
動作原理
式の末尾に ! を付けると、コンパイラは以下のコードを生成したかのように振る舞います。
- 値が 正常 (Ok または 非nil) なら、その値を取り出して処理を続行する。
- 値が エラー (Err または nil) なら、現在の関数から即座にそのエラーを return して抜ける。
fn step1(): __Ret<int, str> { return .Ok(1); }
fn step2(val: int): __Ret<int, str> { return .Ok(val + 1); }
fn series(): __Ret<int, str> {
// step1() が Err なら即座に return .Err(...) する
let res1 = step1()!;
// step2() も同様。正常なら res2 に値が入る
let res2 = step2( res1 )!;
return .Ok( res2 );
}
Nilable との組み合わせ
! 演算子は Nilable 型 (T!) にも使用できます。この場合、現在の関数の戻り値も Nilable である必要があります。
fn getVal(): int! { return nil; }
fn process(): int! {
let val = getVal()!; // getVal() が nil なら即座に return nil
return val + 1;
}
使い分けの指針
| 手法 | 適したケース | 特徴 |
|---|---|---|
| Nilable (T!) | 単純な不在、またはエラー内容が自明な場合。 | 最も軽量で手軽。 |
| 多値返却 (T!, str!) | Lua で一般的なスタイルとの親和性を重視する場合。 | Lua 互換性が高い。! での委譲も可能。 |
| __Ret<T, E> | 複雑なエラー情報を型安全に扱いたい場合。 | 最も堅牢。関数が返す状態が明確になる。 |
エラーの委譲 (
!) を使うことで、正常系(Happy
Path)の処理をメインラインに記述でき、例外処理を背後に隠すことができます。これにより、複雑なビジネスロジックの見通しが劇的に改善されます。