Prompt
文の中のセミコロンのない最後の値は返り値になるけど、
それ以外に言えることはある?
さすが、Rustの「式(Expression)」と「文(Statement)」の境界線に気づいたね!その「セミコロンを省くと返り値になる」というルールは、Rustが単なる手続き型言語ではなく、「式ベース(Expression-based)」の言語であることを象徴する最大の特徴なんだ。
情報工学的に見て、このルールから言える「さらに面白いこと」を3つの視点で深掘りしてみよう!
1. すべてのブロックが「値」を計算する
Rustでは、関数だけじゃなく、if や match、さらには { } で囲まれたただのブロックも、最後にセミコロンをつけなければ「値を返す式」になるんだ。
Rust
let age = 19;
// if自体が値を返すので、変数に代入できる
let status = if age >= 20 {
"大人" // セミコロンなし = 返り値
} else {
"未成年" // セミコロンなし = 返り値
}; // ここは let 文の終わりなのでセミコロンが必要
ここがポイント:
もし if の中でセミコロンをつけてしまうと、そのブロックの返り値は ()(ユニット型、つまり空)になってしまう。だから、Rustでは三項演算子(a ? b : c)が必要ないんだぜ。if がその役割を兼ねているからね!
2. セミコロンは「値を捨てる」演算子である
これ、意外と意識されない視点だけど、Rustにおいてセミコロン ; は**「前の式が評価した値を捨てて、()(ユニット)に変換する」**という命令なんだ。
-
セミコロンなし: 値を次に引き継ぐ。
-
セミコロンあり: そこで値を消費して「おしまい(空)」にする。
例えば、Result を返す関数を呼び出したときに、セミコロンをつけて値を無視しようとすると、Rustコンパイラは「エラーの可能性がある値を捨ててるぜ!」と警告を出してくれる。これは、セミコロンが単なる区切り文字ではなく、**「値を捨てるという明示的なアクション」**だからなんだ。
3. 暗黙の return vs 明示的な return
関数の最後にセミコロンを書かないのは「暗黙の return」だけど、Rustには return キーワードもあるよね。この使い分けの設計思想がまたスマートなんだ。
-
セミコロンなし(暗黙): 関数の「ゴール」に到達した自然な結果。
-
return(明示): ループの途中などで「早期退場(Early Return)」するための緊急手段。
情報工学のコードの読みやすさ(Readability)の観点では、「関数の最後の行に return を書かない」のが Rust 流の美しいスタイルとされているよ。最後にポツンと置かれた式が、その関数が「何を計算するためのものか」を雄弁に物語るからなんだ。
💡 見落とされがちな落とし穴:型の不一致
「最後の行を式にすればいいんだな!」と思って書くと、たまにコンパイラに怒られることがある。
Rust
fn get_number(b: bool) -> i32 {
if b {
return 10; // 明示的な return なので OK
}
100 // セミコロンなし。この関数の最終的な「式」なので OK
}
もしここで、if の枝(Arm)の片方にだけセミコロンをつけたり、型が違う値を返そうとすると、コンパイラは「型が一致しないぜ!」と即座に教えてくれる。これは、ブロック全体が一つの「型」として評価されるからなんだ。
🏁 まとめ
-
Rustは「式」の積み重ね: ほとんどのものが値を返せる。
-
セミコロン
;は「捨てる」合図: 値を次に繋げたいなら書かない。 -
関数の顔: 最後の行(セミコロンなしの式)こそが、その関数の本質的な「成果物」になる。
「セミコロンを打つか打たないか」という指先の動き一つに、これだけのロジックが詰まっているなんて、ワクワクするよね!
次は、この「式ベース」の性質を最大限に活かした、**「match式を使った超絶エレガントな状態遷移」**の書き方を見てみる?😊✨
Would you like me to show you how a complex match expression can replace a messy pile of if-else statements?
Prompt
Rustにyieldはある?
あるならこのセミコロンなし値の話は…別に関係ないか。
結論から言うと、現在の Rust の安定版(Stable)には、Python や JavaScript にあるような yield キーワードは存在しないぜ!
でも、鋭いね!君が言った「セミコロンなしの値」の話と yield は、情報工学的な 「式の評価」と「文脈の保存」 という観点で見事に繋がっているんだ。
1. なぜ yield がないのか?
Rust では、値を次々に生成する仕組みとして Iterator トレイトが確立されているからなんだ。
-
Python/JS:
yieldを使うと、関数の実行を「一時停止」して値を返し、またそこから再開できる(ジェネレータ)。 -
Rust:
next()メソッドを持つ構造体を作ることで、同じことを実現する。
現在、Nightly(開発版)では yield を含む 「ジェネレータ(Generators)」 の機能がテストされているけど、まだ正式採用はされていないんだぜ。
2. 「セミコロンなし値」と yield の意外な関係
君の「関係ないか」という直感、実は半分正解で半分間違いだ!
もし将来 yield が導入されたとしても、Rust の「式ベース(Expression-based)」という性質は変わらない。むしろ、yield 自体も「値を返す式」として設計される可能性が高いんだ。
ジェネレータ(Nightly)での例
実験的な機能では、こんな風に書けるようになっているよ。
Rust
// ※Nightly限定のイメージ
let mut generator = || {
yield 1; // 1を生成して一時停止
yield 2; // 2を生成して一時停止
"おしまい" // 最後の行(セミコロンなし)=ジェネレータの完了時の返り値!
};
ここで「最後のセミコロンなしの値」が生きているのがわかるかな?
-
yield: 途中で「はい、これどうぞ」と渡す一時的な値。 -
最後の式: ジェネレータがすべての仕事を終えた時に、最後に評価された「結果」。
つまり、yield があろうとなかろうと、「ブロックの最後は、そのブロックが最終的に導き出した答え(型)」 という Rust の一貫した美学は守られているんだぜ!
3. 💡 見落とされがちな視点:async/await は yield の親戚
実は、君はもう yield に似たものを触っているかもしれない。それが .await だ!
非同期関数(async fn)の中で .await を呼ぶと、実行がいったん中断されて、準備ができるまで別の処理に道を譲る。これは内部的には、コンパイラが「状態マシン」を作り出し、実質的に yield(一時停止と再開) と同じような動きを自動生成しているんだ。
🏁 まとめ
-
現在:
yieldはない(Iteratorやasyncで代用)。 -
関係性:
yieldがあっても、「最後の式がブロックの値になる」というルールは、関数の最終的な戻り値を定義するために不可欠。 -
思想:
yieldは「一時の立ち寄り」、最後の式は「旅の終わり」。
「セミコロンを打たない」という癖が、将来ジェネレータが安定版に来たときにも、そのまま「完了の合図」として使えるなんて、Rust の設計は本当に一貫していてスマートだよね!
次は、この yield の代わりに使われている 「Iteratorを自作して、自分だけの無限数列を作る」 方法を覗いてみる?それとも、async の裏側でもっとドロドロした状態マシンの話を聴きたい?😊✨
Would you like me to show you how to implement a custom Iterator that behaves like a generator?