レッスン3 — Rust:Result・Option・? 演算子
問い
Rust にはエラーを返す関数が大量にある。それらをすべて手動でアンラップすると、コードが try/catch だらけになる。Rust は ? 演算子で「エラーなら早期 return、成功なら値を取り出す」を 1 文字で書ける。Alloy のコードを読むときに ? が至る所に出てくる理由を理解する。
原理(最小モデル)
Result<T, E>. 成功 =Ok(T)、失敗 =Err(E)、関数の戻り値型として「失敗しうる」を型で表現。Option<T>. 値あり =Some(T)、なし =None、null の代わり、unwrap せずに済む。?演算子.result?=match result { Ok(v) => v, Err(e) => return Err(e.into()) }の糖衣、エラーは呼び出し元へ自動伝播。.await?組み合わせ. async 関数で頻出、.awaitで Future 完了待ち +?でエラー伝播 = 同期的に書ける async コード。unwrap()vs?.unwrap()= エラーで panic(学習 / プロトタイプ)、?= エラーを呼び出し元へ伝播(本番コード)。
具体例 + ステップで組み立てる
Rust:Result・Option・? 演算子
Alloyコードでは、ほぼすべての行に .await? や .parse()? が出る。これは Rustのエラーハンドリング 構文である。
1. なぜ例外がないのか
Rustに try/catch(例外)はない。代わりに 値としてエラーを返す 設計を取る。
- 関数は「成功」か「失敗」を
Result<T, E>で返す - 値があるかないかは
Option<T>で返す
これらは enum(列挙型)である。
enum Result<T, E> {
Ok(T),
Err(E),
}
enum Option<T> {
Some(T),
None,
}
2. Option:あるか、ないか
let v: Vec<i32> = vec![1, 2, 3];
let first: Option<&i32> = v.first(); // Some(&1)
let empty: Vec<i32> = vec![];
let none: Option<&i32> = empty.first();// None
match first {
Some(n) => println!("got {}", n),
None => println!("empty"),
}
3. Result:成功か、エラーか
fn parse_int(s: &str) -> Result<i32, std::num::ParseIntError> {
s.parse::<i32>()
}
match parse_int("42") {
Ok(n) => println!("got {}", n),
Err(e) => println!("oops: {}", e),
}
4. ? 演算子:エラー伝播の魔法
? は 「エラーなら今すぐ関数からreturn、成功なら中身を取り出す」 という意味だ。
fn parse_two(a: &str, b: &str) -> Result<(i32, i32), std::num::ParseIntError> {
let x = a.parse::<i32>()?; // 失敗したら早期リターン
let y = b.parse::<i32>()?; // 失敗したら早期リターン
Ok((x, y))
}
? がない場合、毎回 match を書く必要があり、同じ意味のコードが大きく長くなる。
5. Result<(), Box<dyn Error>> と eyre::Result<()>
main関数の戻り値でよく見るやつです:
| 型 | 意味 |
|---|---|
Result<(), Box<dyn std::error::Error>> | 標準ライブラリのみ(やや冗長) |
eyre::Result<()> | eyre クレートの便利版(推奨) |
eyre は「読みやすいエラー出力」と「多様なエラー型の統合」が特徴である。Alloyコードでは eyre::Result<()> が事実上の標準になる。
6. unwrap() と expect()
学習段階でよく使う「エラーを無視する」方法。本番では使わないでください(パニックします)。
let n: i32 = "42".parse().unwrap(); // 失敗ならパニック
let n: i32 = "42".parse().expect("not an int"); // メッセージ付きパニック
7. これからAlloyで見るパターン
async fn main() -> eyre::Result<()> {
let provider = ProviderBuilder::new()
.connect_http("https://eth.llamarpc.com".parse()?); // ?: parse失敗→return
let block = provider.get_block_number().await?; // ?: RPC失敗→return
Ok(())
}
ほぼすべての行で ? が活躍する。「成功ならそのまま、失敗なら呼び出し元へ返す」 と覚えればよい。
次のレッスンでこれを実際のProvider接続コードに使う。
まとめ(3行)
Result<T, E>成功 / 失敗 +Option<T>値あり / なし = Rust のエラー / 不在表現、type system が「失敗しうる」を強制。?演算子 = エラー時に早期 return、.await?で async + エラー伝播、try/catch の地獄を 1 文字で避ける。unwrap()は学習用 /?は本番、次はProviderでノードへ接続。