Ruby `fetch` unexpected evaluation of params
Ruby fetch
can have an unexpected side-effect which is that the fallback
value gets evaluated even if the key is present. Lets explore that with a code
example.
fetch use-cases
Using fetch
is a common pattern in Ruby, especially when dealing with hashes.
It allows you to retrieve a value for a given key, and if the key is not found,
you can provide a default value. This is useful for avoiding nil
values and
for providing a fallback value.
hash = { a: 1, b: 2 }
hash.fetch(:a) # => 1
hash.fetch(:c, 3) # => 3
fetch fallback values and fallback blocks
When using fetch
, the fallback value is evaluated even if the key is present.
This can lead to unexpected behavior, especially if the fallback value is a
complex expression or a method call.
hash = { a: 1, b: 2 }
hash.fetch(:a, "not found" ) # => 1
hash.fetch(:c, "not found" ) # => "Key not found"
Now with a defined function which throws an exception:
def complex_fallback
raise "This is an exception"
end
hash = { a: 1, b: 2 }
hash.fetch(:a, complex_fallback) # => This is an exception!
This is definitely not what we wanted, as the value a
is present in the hash,
and we’d expect for it to be returned. To avoid this we have to pass a block to
fetch
:
hash = { a: 1, b: 2 }
hash.fetch(:a) { complex_fallback } # => 1
hash.fetch(:c) { complex_fallback } # => This is an exception
Conclusion
If you want to use fetch
with a fallback value, be careful about the
evaluation of the fallback value. If the fallback value is a complex expression or a
method call, it is better to use a block to avoid unexpected evaluation of the
function.