Proc.new の中で return すると LocalJumpError になることがある
Proc(Proc.newやKernel#proc)の中でreturnを使うと、そのProcを囲んでいたメソッドから抜けようとします。呼び出し時にすでにそのメソッドの外にいる場合(トップレベルで呼んだ場合など)、戻る先が無くLocalJumpErrorになります。同じ書き方でもlambdaは自分自身から抜けるだけなので、この問題は起きません。
エラーメッセージの読み方
main.rb:2:in `block in make_greeter': unexpected return (LocalJumpError)
main.rb- ファイル名
2- 行番号 — 実際にクラッシュした行
block in make_greeter- 発生場所 — <main> ならトップレベル、メソッド名ならそのメソッドの中
LocalJumpError- 例外クラス — 何が起きたか。ここを検索するのが最短です
unexpected return- 詳細メッセージ — どの値が問題だったか
このエラーが出る典型パターン
パターン1
1 def make_greeter 2 Proc.new { return "hi" } ^ 3 end 4 5 greet = make_greeter 6 puts greet.call
Procの中のreturnは、Procを定義したメソッドから抜けようとします。呼び出す時点でそのメソッドはすでに終わっているため、戻り先が無くLocalJumpErrorになります。
直し方: Proc.new { return "hi" } を lambda { return "hi" } にします。
パターン2
1 def make_titler 2 Proc.new { return "Sir" } ^ 3 end 4 5 greet = make_titler 6 puts greet.call
lambdaのreturnは自分自身から抜けるだけなので、同じ書き方でもこの問題は起きません。
直し方: Proc.new { return "Sir" } を lambda { return "Sir" } にします。
パターン3
1 def make_labeler 2 Proc.new { return "OK" } ^ 3 end 4 5 greet = make_labeler 6 puts greet.call
Proc.newで作った手続きは、生成元のメソッドが生きている間しかreturnを使えません。
直し方: Proc.new { return "OK" } を lambda { return "OK" } にします。
パターン4
1 def make_tagger 2 Proc.new { return "new" } ^ 3 end 4 5 greet = make_tagger 6 puts greet.call
生成したProcをメソッドの外に持ち出して後から呼び出す設計では、Proc.newは危険です。
直し方: Proc.new { return "new" } を lambda { return "new" } にします。
パターン5
1 def make_marker 2 Proc.new { return "done" } ^ 3 end 4 5 greet = make_marker 6 puts greet.call
戻り値だけが欲しい単純な処理を書きたい場合、無名関数はProcではなくlambdaを使うのが安全です。
直し方: Proc.new { return "done" } を lambda { return "done" } にします。
よくある誤解
「Procとlambdaはどちらも無名関数だから、returnの挙動も同じはず」という思い込みは誤りです。lambdaのreturnはlambda自身から抜けるだけですが、Procのreturnは定義時に囲んでいたメソッドごと抜けようとするため、状況によってはLocalJumpErrorになります。
まとめ
Proc.new の中で return すると LocalJumpError になることがあるは上級でつまずきやすい項目です。上の5パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。