Ruby's File.open and the need for f.close
Asked Answered
I

5

105

It's common knowledge in most programming languages that the flow for working with files is open-use-close. Yet I saw many times in ruby codes unmatched File.open calls, and moreover I found this gem of knowledge in the ruby docs:

I/O streams are automatically closed when they are claimed by the garbage collector.

darkredandyellow friendly irc take on the issue:
[17:12] yes, and also, the number of file descriptors is usually limited by the OS
[17:29] I assume you can easily run out of available file descriptors before the garbage collector cleans up. in this case, you might want to use close them yourself. "claimed by the garbage collector." means that the GC acts at some point in the future. and it's expensive. a lot of reasons for explicitly closing files.

  1. Do we need to explicitly close
  2. If yes then why does the GC autoclose ?
  3. If not then why the option?
Italianize answered 25/1, 2011 at 15:38 Comment(4)
Your 'common knowledge' has been outdated since destructors were invented.Colophon
@meager: When were destructors invented?Barone
Just a note: While file descriptors are limited, on Linux at least, the limit is quite high.Erickericka
@Erickericka : on my ubuntu12.04 $ ulimit -n => 1024 it's only high when you do just some simple job. Bad habit will cause big problem one day!Progenitor
L
145

I saw many times in ruby codes unmatched File.open calls

Can you give an example? I only ever see that in code written by newbies who lack the "common knowledge in most programming languages that the flow for working with files is open-use-close".

Experienced Rubyists either explicitly close their files, or, more idiomatically, use the block form of File.open, which automatically closes the file for you. Its implementation basically looks something like like this:

def File.open(*args, &block)
  return open_with_block(*args, &block) if block_given?
  open_without_block(*args)
end

def File.open_without_block(*args)
  # do whatever ...
end

def File.open_with_block(*args)
  yield f = open_without_block(*args)
ensure
  f.close
end

Scripts are a special case. Scripts generally run so short, and use so few file descriptors that it simply doesn't make sense to close them, since the operating system will close them anyway when the script exits.

Do we need to explicitly close?

Yes.

If yes then why does the GC autoclose?

Because after it has collected the object, there is no way for you to close the file anymore, and thus you would leak file descriptors.

Note that it's not the garbage collector that closes the files. The garbage collector simply executes any finalizers for an object before it collects it. It just so happens that the File class defines a finalizer which closes the file.

If not then why the option?

Because wasted memory is cheap, but wasted file descriptors aren't. Therefore, it doesn't make sense to tie the lifetime of a file descriptor to the lifetime of some chunk of memory.

You simply cannot predict when the garbage collector will run. You cannot even predict if it will run at all: if you never run out of memory, the garbage collector will never run, therefore the finalizer will never run, therefore the file will never be closed.

Lurdan answered 25/1, 2011 at 16:4 Comment(7)
github.com/isaac/sunspot/blob/cell/sunspot/lib/sunspot/… +23 (although its Kernel#open and used primarily for the HTTP side of it, but I reached it with a local file path parameter, nevertheless... ; I am still trying to find time to patch&request-pull), github.com/jnicklas/carrierwave Ctrl+f "File.open" (it's given as an example, but in a bad way...), and several other places I don't remember. I have a beef with the issue because of stability requirements in my projects..Italianize
In this example, should raise be inside the rescue block? Won't this just throw a runtime error if raise is called and there is no exception?Installation
@JeffStorey: nice catch! 17 months unnoticed …Debag
@JörgWMittag and now 17 more months not fixed :P I guess the main point here is ensure, rescue and raise are not necessary at all.Atom
I think you can't have ensure without rescue. And you can't just silently swallow the exception, you need to propagate it to the caller, after having closed the file. Anyway, remind me again in May '15 :-DDebag
You can have an ensure clause without a rescue. def foo; begin; return 1; ensure; return 2; end; end; foo returns 2.Orchardman
Very good hint to solve my problem. Within a single ruby run, I had to make sure my files are properly closed before accessing them again.Cookbook
C
78

You should always close file descriptors after use, that will also flush it. Often people use File.open or equivalent method with blocks to handle file descriptor lifetime. For example:

File.open('foo', 'w') do |f|
    f.write "bar"
end

In that example the file is closed automatically.

Cimbalom answered 25/1, 2011 at 15:47 Comment(2)
Good point. I tracked a bug to a script which does not call File.close. As a result the last line will be missing in some files now and then.Unwind
Outstanding. I never knew this trick. Much like java-8 in that regard. Thank you.Ibrahim
D
2

According to http://ruby-doc.org/core-2.1.4/File.html#method-c-open

With no associated block, File.open is a synonym for ::new. If the optional code block is given, it will be passed the opened file as an argument and the File object will automatically be closed when the block terminates. The value of the block will be returned from File.open.

Therefore, will automatically be closed when the block terminates :D

Deflation answered 26/5, 2016 at 17:31 Comment(0)
M
1
  1. Yes
  2. In case you don't, or if there is some other failure
  3. See 2.
Magnitogorsk answered 25/1, 2011 at 15:40 Comment(0)
V
-4

We can use the File.read() function to read the file in ruby..... such as,

file_variable = File.read("filename.txt")

in this example file_variable can have the full value of that file....

Valdemar answered 10/1, 2015 at 12:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.