LoginSignup
22
14

More than 5 years have passed since last update.

ブロック付きメソッドによる安全なリソース管理を破壊する

Last updated at Posted at 2017-06-01

Rubyにおける良いスタイルとして、ブロック付きメソッドでリソースを自動管理するという手法がある。
この安全性を破壊してみよう。

前提知識

まず、確認として次を見てみよう。

File.open("foo.txt") {|f|
  f.read(3)
}

1行目で開いたFileオブジェクトfは3行目で自動的にcloseされる。ブロック中の制御フローが多少複雑でも、例外が発生しても確実に3行目でcloseされる。
最近では他の様々なメジャーな言語が独特の構文で似た機能を提供しているから、10年前とは異なり特にRubyの目立つ機能というわけでもなくなったが、とにかくブロックによるリソース管理は良いものだ。

しかし、安全は不自由でもある。ブロック終了時点で確実にファイルを閉じてしまうため、File.openを呼んだメソッドが終了したあとでf.readを呼ぶことはできない。

def open_file(name)
  File.open(name) {|f| return f }
end

f = open_file("foo.txt")
f.read(3)  # IOError: closed stream

破壊への道のり

時として、人はそれでも自由を必要とする。そんな時はちょっとしたトリックで自動リソース管理を破壊しよう。

def open_file(name)
  iter = File.enum_for(:open, name)
  return iter.next, ->{ loop { iter.next } }
end

f, close = open_file("foo.txt")
f.read(3)
close.call  # ちゃんとファイルを閉じるのは自己責任

これで安全性を取っ払って自由にリソースリークできる。

10000.times.map do
  f, _ = open_file("foo.txt") # Errno::EMFILE: Too many open files @ rb_sysopen - foo.txt
  [f, f.read(3)]
end

終わりに

実のところ、File.openの場合なら単にブロック付きでないバージョンを呼べば良い。しかし、場合によってはそういう安全でないAPIは提供されていないこともある。また、安全でないAPIはコーディング規約で利用が禁止されているかもしれない。
そういうやむを得ない場合に、それでもどうしてもリソースをリークさせる必要があるときに本稿が役に立てば幸いである。

22
14
0

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
14