例外処理におけるfinally（ensure）は意味ない……なんて思ったら大間違い

# はじめに

この記事では各言語でよく使われる「例外処理」における`finally`（Rubyでは`ensure`）という構文の使い方と必要性について説明します。

例外処理に関しては殆どの言語で通用する話なので、今回はどの特定の言語の話でもなく、できるだけ一般的に語るつもりですが、説明するためのサンプルコードはDart、Python、Rubyで表示することにします。


# 各言語の例外処理

色んな言語には「例外処理」という構文があります。その中で殆どは「`try` `catch` `finally` `throw`」と表記しますが、PythonとRubyなど違う表記が使われます。

各言語の例外処理の表記を纏めてみるとこのようになります。

<table>
<tr><th style="text-align: center;">言語</th><th>最初に実行</th><th>例外があれば実行</th><th>最後に絶対実行</th><th>例外を引き起こす</th></tr>
<tr></tr>
<tr><th>C#</th><td rowspan="13"><font color="#ff5522">try</font></td><td rowspan="11"><font color="#ff5522">catch</font></td><td rowspan="13"><font color="#ff5522">finally</font></td><td rowspan="11"><font color="#ff5522">throw</font></td></tr>
<tr></tr>
<tr><th>Java</th></tr>
<tr></tr>
<tr><th>JavaScript</th></tr>
<tr></tr>
<tr><th>TypeScript</th></tr>
<tr></tr>
<tr><th>PHP</th></tr>
<tr></tr>
<tr><th>Dart</th></tr>
<tr></tr>
<tr><th>Python</th><td><font color="#ff5522">except</font></td><td rowspan="3"><font color="#ff5522">raise</font></td></tr>
<tr></tr>
<tr><th>Ruby</th><td><font color="#ff5522">begin</font></td><td><font color="#ff5522">rescue</font></td><td><font color="#ff5522">ensure</font></td></tr>
</table>

簡潔のためにここからは一番一般的である「`try` `catch` `finally` `throw`」だけ表記します。Rubyを使っている方は頭の中で変換しておいてください。

実際にRubyの`begin`文は例外処理のこと以外にも使われますし、`begin`なしで`rescue`と`ensure`だけ使うこともできますが、これについては本題と関係ないので省略します。

以下はDart、Python、Rubyのコードで解説しますが、Dartの例外処理文はJavaScript、TypeScript、C#、Java、PHPとも大体似ているので、これらの言語を使っている方はDartのコードを見たら理解できると思います。


# finallyは要らなくない？

まずこの記事を書くきっかけから説明します。

普段例外処理を行う時にこのように`try`と`catch`を使いますね。
```dart:dart
try {
  // 何かの実行
}
catch (e) {
  // try節でエラーが出たらここは実行する
}
```
```python:python
try:
    # 何かの実行
except:
    # try節でエラーが出たらここは実行する
```
```ruby:ruby
begin
  # 何かの実行
rescue
  # try節でエラーが出たらここは実行する
end
```

こう書いたら`try`節の中で何かエラーが発生したらそのエラーがすぐ処理されて実際に現れず、その代わりに`catch`節で書いた内容が実行される、という仕組みになりますね。

普段はこれだけ使うのは殆どですが、それに加えて`finally`節をくっつけることができますが、これは必須ではないので、使われる場面が少ないはずです。例外処理を書いたことがあっても`finally`を使ったことない人も多いのではないでしょうか。

`finally`の意味は「エラーが出ても出なくても実行する」と説明する記事や教科書も多いようですね。その説明は間違いなわけではないが、全然足りないのです。

もしそれだけだったら`finally`の必要性を全然感じません。

なぜならわざわざ`finally`を書かなくても`try`と`catch`節の下に書いたら普段は実行されるから。

例えば：
```dart:dart
try {
  print("やるぞ！");
  // エラーが懸念される実行
  throw Exception();
}
catch (e) {
  print("エラーが出た！");
  // 例外が起きたら実行すること
}
finally {
  print("やっと終わった");
  // ここは例外起きても起きなくても必ず実行する
}

print("次は……");
// ここも例外起きても起きなくても実行するはず
```
```python:python
try:
    print("やるぞ！")
    # エラーが懸念される実行
    raise Exception
except:
    print("エラーが出た！")
    # 例外が起きたら実行すること
finally:
    print("やっと終わった")
    # ここは例外起きても起きなくても必ず実行する
print("次は……")
# ここも例外起きても起きなくても実行するはず
```
```ruby:ruby
begin
  puts "やるぞ！"
  # エラーが懸念される実行
  raise
rescue
  puts "エラーが出た！"
  # 例外が起きたら実行すること
ensure
  puts "やっと終わった"
  # ここは例外起きても起きなくても必ず実行する
end
puts "次は……"
# ここも例外起きても起きなくても実行するはず
```

つまり、例外問わず次に実行したいことはわざわざ`finally`節を作って中に書く意味はありません。

確かに`finally`で書いた方が`try`の内容と密接な関係があるとわかりやすいかもしれませんが、それだけのことでしょうね。

こういうことなので、私も最初に勉強した時に`finally`の必要性を感じなくて無視していましたが、結局これは色んな言語に搭載されている機能だからやはり何か意味があるはずでしょう。

実はPHPだって最初は`finally`がなかったが、バージョン5.5から追加されたらしいです。

C++でも`finally`はないが、C#になると`finally`があります。

その必要性について察して、色々検索したらやっとわかりました。

以下は私が見つけた`finally`の本当の意味を解説していきます。


# catchの中で例外が発生する場合

普段`try`節の中でエラーが出たら`catch`節に入ることでエラーが治まって、そこで例外処理が始まりますが、もしその`catch`の中でまた何かのエラーが出たらどうなりますか？

この場合はやはり実際にエラーが出てプログラムは止まってしまいますね。そしてその下に書いたコードは当然ながら実行されません。

しかし`finally`節の中に書いたコードは、こんな場合でも実行されます！

例えば：
```dart:dart
try {
  throw Exception();
}
catch (e) {
  throw e;
}
finally {
  print("ここは実行される");
}
print("ここは実行されない");
```
```python:python
try:
    raise Exception
except Exception as e:
    raise e
finally:
    print("ここは実行される")
print("ここは実行されない")
```
```ruby:ruby
begin
  raise
rescue => e
  raise e
ensure
  puts "ここは実行される"
end
puts "ここは実行されない"
```

この例で`finally`を使う意味はやっとはっきりとわかりますね。

結局エラーが発生してプログラムが止まることに変わりないのですが、エラーが出る前に`finally`節は実行されます。

「`finally`は例外が起きても起きなくても必ず実行される」という説明ですが、これは`catch`の中で例外が起きる場合も含まれます。

例外処理のことを教える時に`finally`を話に出したらこの場合の説明も必要だと思います。ここまで説明しないと`finally`を使う意味は伝わらないから。

だから「例外処理の途中でまた例外が起きても必ず最後に実行する部分」という説明の方が`finally`の適切な解説だと思います。

こうやって`finally`を使うと、最悪の場合でも何があっても必ず実行されるという保証にもなります。

どうしても実行しておきたい大切なことは`finally`に書いたらいいかもしれません。

なお、世の中何が起きるかわからないから、結局`finally`を使っても実行されない場合はいくつかあるでしょう。

例えばこの記事の場合です。

https://qiita.com/mtsmfm/items/c967e0624bbafb25f2a3


# catchを省略する場合

以上`finally`の必要性について説明しましたが、実際に`finally`に関して説明しておくべきことがまだあります。

まず実は`finally`を書くと、`catch`を書かなくてもいいということになっているのです。

普段`try`文を使う時、`try`だけ書いてはいけず、`catch`もセットで一緒にしますね。たとえ処理したいことがなくても空っぽの`catch`を使う必要があります。
```dart:dart
try {
  // ここで何かする
}
catch (e) {}
```
```python:python
try:
    # ここで何かする
except:
    pass
```
```ruby:ruby
begin
  # ここで何かする
rescue
end
```

ここで`catch`の部分を書かないと構文エラーになります。

（なお、Rubyでは`begin`だけで問題ないが、そう書いても例外処理にならない）
```ruby:ruby
begin
  raise # 構文エラーではないが、普通にエラーが出る
end
```

ただし、実はこれは`catch`ではなく、`finally`でもいいです。つまりこう書いたら`catch`がなくても構文エラーになりません。
```dart:dart
try {
  // ここで何かする
}
finally {}
```
```python:python
try:
    # ここで何かする
finally:
    pass
```
```ruby:ruby
begin
  # ここで何かする
ensure
end
```

ただし`catch`がないから、結局`try`節の中でエラーが発生した場合、処理されずそのままエラーになります。

`finally`はあくまで必ず実行することを確保するためであり、エラーを止める力がありません。

ということで、「エラーになるならそのままエラーにしてもいいが、それでも必ずやっておきたいことがある」という場合に使うです。

もしこう書いたら
```dart:dart
try {
  // 何かの実行
}
finally {
  // try節でエラーが出ても実行しておきたいこと
}
```
```python:python
try:
    # 何かの実行
finally:
    # try節でエラーが出ても実行しておきたいこと
```
```ruby:ruby
begin
  # 何かの実行
ensure
  # try節でエラーが出ても実行しておきたいこと
end
```

それはこう書くのと同じ。
```dart:dart
try {
  // 何かの実行
}
catch (e) {
  // try節でエラーが出ても実行しておきたいこと
  throw e;
}
```
```python:python
try:
    # 何かの実行
except Exception as e:
    # try節でエラーが出ても実行しておきたいこと
    raise e
```
```ruby:ruby
begin
  # 何かの実行
rescue => e
  # try節でエラーが出ても実行しておきたいこと
  raise e
end
```

ということでデバッグの時など、エラーを取り消したくはない場合`try` `catch`より`try` `finally`を使った方がいいかもしれません。


# finallyが関数内で使われる場合

もう一つ気をつけるべきなのは関数内で`finally`使う場合です。

もしエラーが発生するなら`finally`を使ってもそのまま発生して止めることはできない……と、上記でそう説明しましたが、関数の中で使う場合話はまたややこしくなります。

実はもし関数内で`finally`節の中で`return`したら、エラーが消えて通常に動くことになります。

例えば：
```dart:dart
kansuu() {
  try {
    throw Exception();
  }
  finally {
    return;
  }
}

main() {
  kansuu();
}
```
```python:python
def kansuu():
    try:
        raise Exception
    finally:
        return

kansuu()
```
```ruby:ruby
def kansuu
  begin
    raise
  ensure
    return
  end
end

kansuu
```

こんな関数にエラーは出ません。

`catch`を使ってその中でエラーが出る場合でも同じようにエラーが取り消されます。

このように`finally`節の中で`return`を書いたら絶対にエラーが出ない関数になります。（Dartの場合`main()`の中でも通用します）

ただし関数の中の`finally`節で`return`の使用を避けるべきという主張の人もいるから、こんな使い方はしない方がいいと思います。


# tryでreturnする場合

もし関数の中で`try` `finally`を使って、`try`の中で`return`が実行される場合でも`finally`節の内容も実行される。
```dart:dart
kansuu() {
  try {
    return;
  }
  finally {
    print("ここは実行される");
  }
}

main() {
  kansuu();
}
```
```python:python
def kansuu():
    try:
        return
    finally:
        print("ここは実行される")

kansuu()
```
```ruby:ruby
def kansuu
  begin
    return
  ensure
    puts "ここは実行される"
  end
end

kansuu
```

この場合`finally`節に書くのは`return`の前に書くのと同じような意味になりますが、`finally`節の中で書くことで、もし関数の中で例外が起きた場合でも必ず実行する、ということになります。


# catchでreturnする場合

`try`で`return`がある場合と同じように、`catch`節の中で`return`する場合も`finally`は実行されます。
```dart:dart
kansuu() {
  try {
    throw Exception();
  }
  catch (e) {
    return;
  }
  finally {
    print("ここは実行される");
  }
}

main() {
  kansuu();
}
```
```python:python
def kansuu():
    try:
        raise Exception
    except:
        return
    finally:
        print("ここは実行される")

kansuu()
```
```ruby:ruby
def kansuu
  begin
    raise
  rescue
    return
  ensure
    puts "ここは実行される"
  end
end

kansuu
```


# finallyのreturnは優先される

ではもし`try`の中でも`catch`の中でも`finally`の中でも`return`がある場合どうなるか、試してみませんか。
```dart:dart
kansuu(x) {
  try {
    if (x == 0)
      return "頑張る";
    else
      throw Exception();
  }
  catch (e) {
    return "エラー";
  }
  finally {
    return "終わり";
  }
}

main() {
  print(kansuu(0)); // 終わり
  print(kansuu(1)); // 終わり
}
```
```python:python
def kansuu(x):
    try:
        if(x == 0):
            return "頑張る"
        else:
            raise Exception
    except:
        return "エラー"
    finally:
        return "終わり"

print(kansuu(0)) # 終わり
print(kansuu(1)) # 終わり
```
```ruby:ruby
def kansuu(x)
  begin
    if(x == 0)
      return "頑張る"
    else
      raise
    end
  rescue
    return "エラー"
  ensure
    return "終わり"
  end
end

puts kansuu(0) # 終わり
puts kansuu(1) # 終わり
```

結局`finally`の中の`return`はどうしても優先されます。

`finally`は必ず実行されるから、たとえその前に`try`や`catch`で`return`が行われても、最終的に`finally`が`return`すれば上書きされます。

このように`finally`は最強ですね。


# 終わりに

以上の解説と例で`finally`が使われる意味はわかるようになったでしょうか。

最初は意味ないと思っていたものは実は意外と意味深いもので面白いですよね。

確かに`finally`の出番は少ないです。私も今まで使ったことありません。

それでも自分の書いたコードで使うかどうかは別として、誰かのコードの中で`finally`が使われたらどんな結果が招かれるか理解しておく必要があるでしょう。


# 参考＆もっと読む

https://qiita.com/ktarow/items/9d8f3217bb148f2e51d2

https://qiita.com/k-penguin-sato/items/1a6c8096effccb19565a

https://qiita.com/mofuko0213/items/1dba74db52fe7e9b8b20

https://qiita.com/ktarow/items/f4f971404d62bfd9d4f4

https://qiita.com/lemonade_37/items/c2fc02b5159a751dc55b

https://qiita.com/logger/items/b3e2f6e9f251fab235ef

https://qiita.com/SysEngKuma1302/items/ad9d059ae0b13b201785

https://qiita.com/eijenson/items/7e9e112e69b37f72353c

https://qiita.com/imuimu/items/7f27d156dd0f6507803b

https://qiita.com/namn1125/items/ff55d1112039848a8290

https://qiita.com/gyu-don/items/cfe718a8542f4d252406

https://qiita.com/qualitia_cdev/items/f536002791671c6238e3

http://blog.tojiru.net/article/234566904.html

https://qiita.com/SuguruOoki/items/32bec26330da3ab1bf66

https://qiita.com/my_programming/items/9ba25114ef217d077ca5

https://qiita.com/mik_claire/items/8f36d143639cd538a980

https://qiita.com/javacommons/items/0befadc740c7f01052c3