23

I recently learned that you can use rescue on a line of code in case something goes wrong on that line (see http://www.rubyinside.com/21-ruby-tricks-902.html Tip #21). I have some code that used to look like this:

if obj['key'] && obj['key']['key2'] && obj['key']['key2']['name']
  name = obj['key']['key2']['name']
else
  name = ''
end

With the rescue method, I believe I can change that code into something like this:

name = obj['key']['key2']['name'] rescue ''

If a nil exception is thrown at any level of accessing the hash, it should get caught by the rescue and give me '', which is what I want. I could also choose to set name to nil if that were the desired behavior.

Is there any known danger in doing this? I ask because this seems too good to be true. I have so much ugly code that I'd love to get rid of that looks like the first code example.

Jimmy Z
  • 723
  • 1
  • 5
  • 18
  • For that particular use, an inline `rescue` is safe. Be *VERY* careful using it at the end of a method call, where something else could raise an exception, perhaps because of an I/O error, or missing database information. Debugging those situations is really, really, hard and can make you nuts. – the Tin Man Mar 13 '13 at 22:05

2 Answers2

17

Reads good! But it will hit your performance. In my experience rescue is much slower when triggered and slightly slower when it's not. In all cases the if is faster. Other thing to consider, is that exceptions shouldn't be expected and you kind of are with this code. Having a hash so deeply nested might be a good smell that a refactoring is nede

Leonel Galán
  • 6,993
  • 2
  • 41
  • 60
  • The hashes come from a web service that returns deeply nested JSON. I have to check for nils all along the way because the service often leaves the nested elements nil. – Jimmy Z Mar 13 '13 at 21:49
  • If this is the case I recommend you use a gem to enhance your hashes, to make your code more readable: https://github.com/intridea/hashie. – Leonel Galán Mar 13 '13 at 21:52
  • Particularly, Mash (https://github.com/intridea/hashie#mash) see the examples for multilevel testing, it might not be faster than ifs, but it will improve your code! – Leonel Galán Mar 13 '13 at 21:54
14

This specific example can now be achieved with Ruby 2.3's dig method.

name = obj.dig 'key', 'key2', 'name'

This will safely access obj['key']['key2']['name'], returning nil if any step fails.

(In general, it's usually advised to use exceptions only for real, unanticipated, errors, though it's understandable in an example like this if the syntax makes it cumbersome.)

mahemoff
  • 44,526
  • 36
  • 160
  • 222