22
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【Rubyアンチパターン】ensure節でreturnしてはならない

Last updated at Posted at 2018-06-30

ensureでreturnすると...

rubyで例外処理を書くとき、ensureに「return」を書いてはいけません。

# ヤバい例
def div(x, y)
  puts "=== 計算開始 ==="
  ret = {}
  ret[:answer] = x / y
  ret[:message] = "割り算成功!"
rescue ZeroDivisionError => e
  ret[:answer] = nil
  ret[:message] = "割り算失敗..."
ensure
  puts "=== 計算終了 ==="
  return ret
end

なぜなら、ensureでreturnすると、rescueで指定していない例外ももみ消すという恐ろしい挙動をするからです。
下記の実行例を見てください。

# 実行例1
div(10,2)
=== 計算開始 ===
=== 計算終了 ===
#=> {:answer=>5, :message=>"割り算成功!"}

# 実行例2
div(10,0)
=== 計算開始 ===
=== 計算終了 ===
#=> {:answer=>nil, :message=>"割り算失敗..."}

# 実行例3
div(10,"abc")
=== 計算開始 ===
=== 計算終了 ===
#=> {}

実行例1・2は、想定どおりの動作かも知れません。
でも、実行例3はどうでしょうか?
「10 ÷ "abc"」は計算できないので、TypeErrorが発生しそうに思えますが、実際は例外が発生せず、空のハッシュが返されています。

このようなコードは、出るべき例外を隠してしまうので、バグの温床となる上、デバッグがしづらくなり、とても厄介です。

改善例

ensureでreturnするのではなく、本体とrescueの最後に、戻り値に指定したい値を書くようにしましょう。

# 改善例
def div(x, y)
  puts "=== 計算開始 ==="
  ret = {}
  ret[:answer] = x / y
  ret[:message] = "割り算成功!"
  return ret
rescue ZeroDivisionError => e
  ret[:answer] = nil
  ret[:message] = "割り算失敗..."
  return ret
ensure
  puts "=== 計算終了 ==="
end
# 実行例1
div(10,2)
=== 計算開始 ===
=== 計算終了 ===
#=> {:answer=>5, :message=>"割り算成功!"}

# 実行例2
div(10,0)
=== 計算開始 ===
=== 計算終了 ===
#=> {:answer=>nil, :message=>"割り算失敗..."}

# 実行例3
div(10,"abc")
=== 計算開始 ===
=== 計算終了 ===
TypeError: "abc" can't be coerced into Integer

実行例1・2の動作は変わらず、実行例3の問題が解決されているのが分かると思います。

補足:言語仕様について

divメソッドは、必ず最後に

  puts "=== 計算終了 ==="

を処理するため、戻り値がnilになってしまうのでは、と考える方もいるかも知れませんが、心配無用です。
rubyは、言語仕様として、ensure節の評価値を無視するようになっているからです。

begin式全体の評価値は、本体/rescue節/else節のうち 最後に評価された文の値です。また各節において文が存在しなかったときの値 はnilです。いずれにしてもensure節の値は無視されます。

ちなみに、上記の仕様は、「Ruby技術者認定試験 Silver」でも問題として出題されます。
Ruby技術者認定試験で問われる知識は、他にも実践で役立つものが多い印象です。
ruby初心者で、まだ受けたことのない人は、是非1度受けてみると良いと思います。

22
6
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?