From: Robert Klemme Date: 2010-01-08T06:55:14+09:00 Subject: Re: ruby constructor return value? On 01/07/2010 09:13 PM, Thom Wharton wrote: > Thomas Hurst wrote: >> * Brendan Stennett (brendan6@gmail.com) wrote: >> >>> if o = Zip.new('00000') >>> //this block execute whether or not that zip code exists >>> else >>> //never happens >>> end >> Normally you'd use an exception; e.g. make a ZipCodeNotFound class >> inherited from StandardError and rescue it. You could even do: >> >> o = Zip.new(..) rescue nil >> >> And this will rescue the exception and return nil. It will also eat any >> other StandardError, so beware. If you want users of the class to be >> able to just check for nil/false, make a factory method: >> >> class Zip >> def self.find(zip) >> new(zip) >> rescue ZipCodeNotFound >> false >> end >> end >> >> o = Zip.find('..') >> >> However, new is just a method like any other; you can override it if you >> really want it to potentially return nil: >> >> class Zip >> def self.new(*args) >> o = allocate >> if o.__send__(:initialize, *args) >> return o >> else >> return nil >> end >> end >> end >> >> Allocate will make a new object without calling the constructor, you can >> then call initialize yourself (using __send__ as it's a private method) >> and conditionally return your new object. >> >> Since this may be surprising behavior for .new I don't really recommend >> it, though. > > Is this info still valid? More specifically, would I have to create a > new method for my class to have it return nil? And would my class > initializer method have to raise an exception or could it return nil if > the construction fails? For failed construction you need to throw an exception. Everything else is pretty useless. Btw, even the "trick" shown above with evaluating the return value of #initialize creates the object even in error cases. IMHO there is really no point in doing that - the example above merely demonstrates that you can react on the return value of #initialize if you like to. > Basically, I am trying to get new to return nil if construction of the > object fails. I have a strong background in C++ coding, and one thing > that has always irked me about the language is that constructors cant > fail -- the object is always created regardless of whether or not it can > be properly constructed. Hmm, that's only part of the story: if it is created on the stack nobody will be able to access it in case the constructor throws. For objects allocated on the heap I am not so sure but I believe the allocation needs to be reversed in case of an exception as well; so even if you would leak this to somewhere else (say a global variable) it would not be of any use - in fact, that would be a dangerous thing to do. On a more general level, there is no alternative approach: you cannot test all the conditions beforehand. It's the same as testing whether a remote server can be connected: the test can succeed yet the "real" connection can still fail (e.g. because the server crashed after checking). So, you have to create the object and in case of exceptions revert it. > Btw, I've noticed that the File class will return nil if you call the > new method with a filename that doesnt refer to an existing file. Does > the File class have its own new/initialize methods? If so, is that code > available online for examination? Not sure what you are referring to but File.new and File.open will throw an exception if they fail: robert@fussel:~$ ruby19 -e 'p File.new("not_existent")' -e:1:in `initialize': No such file or directory - not_existent (Errno::ENOENT) from -e:1:in `new' from -e:1:in `
' robert@fussel:~$ Kind regards robert -- remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/