Hash#fetch
Overview
Hash#fetch retrieves the value for a given key, just like h[key]. The difference is what happens when the key is missing. Where h[key] returns nil, fetch gives you three options: raise an error, return a default value, or run a block.
This matters because nil is a valid hash value. If you store h[:answer] = nil and then check h[:answer] expecting it to mean “key not found”, you get a false positive. fetch disambiguates between a missing key and a nil value.
Signature
fetch(key) -> object
fetch(key, default) -> object
fetch(key) { |key| block } -> object
Parameters
| Parameter | Type | Description |
|---|---|---|
key | object | The key to look up. |
default | object | Value to return if key is not found. Optional. |
| block | proc | Block evaluated with the key as argument if key is not found. Optional. |
Return Value
The value associated with key, the default value, or the block’s return value.
Raises KeyError if key is not found and no default or block is given.
Basic Usage
Raise on missing key
The most common form:
config = { host: "localhost", port: 5432 }
config.fetch(:host) # => "localhost"
config.fetch(:port) # => 5432
config.fetch(:user) # => KeyError: key not found: :user
Providing a default value
Pass a second argument as the fallback:
options = { theme: "dark" }
options.fetch(:language, "en") # => "en"
options.fetch(:theme, "light") # => "dark"
The default is returned only when the key is absent, not when the value is nil:
h = { key: nil }
h.fetch(:key, "default") # => nil (the stored nil, not "default")
Using a block
Pass a block and it runs only when the key is absent:
data = { api_version: "v2" }
data.fetch(:timeout) { |k| puts "#{k} not found, using default"; 30 }
# => "timeout not found, using default"
# => 30
The block receives the missing key as its argument, which is useful for building dynamic defaults:
h = {}
h.fetch(:cache_ttl) { |k| ENV["DEFAULT_#{k.upcase}"] || 300 }
Comparison with h[key]
h = { answer: nil }
h[:answer] # => nil (could mean missing, could mean stored nil)
h.fetch(:answer) # => nil (raises KeyError if you want to distinguish)
h[:missing] # => nil (silent failure — easy to miss)
h.fetch(:missing) # => KeyError (you know something is wrong)
Common Use Cases
Configuration with required keys
class AppConfig
def initialize(raw)
@config = raw
end
def database_url
@config.fetch(:database_url) do
raise "database_url is required — set it in your environment"
end
end
end
AppConfig.new({}).database_url
# => raises "database_url is required"
Safe API response parsing
response = JSON.parse(http_response.body)
user_id = response.fetch("user", {}).fetch("id", nil)
created_at = response.fetch("user", {}).fetch("created_at", nil)
Defaults that differ per key
DEFAULTS = { timeout: 5, retries: 3, debug: false }
def fetch_option(key)
options.fetch(key) { |k| DEFAULTS.fetch(k) }
end
opts = { timeout: 10 }
fetch_option(:timeout) # => 10 (from options)
fetch_option(:retries) # => 3 (from DEFAULTS)
fetch_option(:unknown) # => KeyError (not in either)
Fetch with Multiple Values
Ruby also provides Hash#fetch_values, which fetches multiple keys at once and raises KeyError for any missing key:
user = { name: "Alice", email: "alice@example.com", role: "admin" }
user.fetch_values(:name, :email)
# => ["Alice", "alice@example.com"]
user.fetch_values(:name, :missing_key)
# => KeyError: key not found: :missing_key
This is useful when you need several values in one call and want to fail fast if any are absent.
Gotchas
Block and default argument together. If you pass both a default argument and a block, the block is ignored:
h = {}
h.fetch(:k, "default") { |k| "block result" }
# => "default" (block is never called)
Subtle difference between fetch and h[key] when key exists with nil value. As shown above — fetch does not treat a stored nil as missing. Only an absent key triggers the default or the block.
Calling fetch on a hash subclass. Hash#fetch works normally on subclasses like HashWithIndifferentAccess from ActiveSupport.
See Also
- /reference/hash-methods/has-key/ — check whether a key exists without retrieving its value
- /reference/hash-methods/dig/ — safely dig into nested hashes and arrays
- /reference/hash-methods/values/ — get all values from a hash