In Ruby, what does the hash literal {title:, rating:} expand to, and where does each value come from?
answer
- value omission
- same as {title: title}
- local variable first, then method
- NameError when neither exists
- added in Ruby 3.1
basics
~20 s{title:, rating:} is shorthand for {title: title, rating: rating}: each key is a symbol and its value is what the bare name evaluates to, a local variable if one exists, otherwise a method call. It needs Ruby 3.1 or later.
solid answer
~40 sRuby 3.1 added **value omission** in hash literals: `{title:, rating:}` means `{title: title, rating: rating}`. The keys are symbols, and each value is resolved exactly like the bare identifier would be: a **local variable** of that name if one is in scope, otherwise a **method call** on `self`, such as an `attr_reader`; constant names work too, so `{Limit:}` is `{Limit: Limit}`. If nothing by that name exists, the literal raises `NameError`. Shorthand and explicit pairs mix freely: `{title:, rating: rating.to_i}`. It only works in the `name:` symbol-key form; a `"title" =>` pair still needs a value. The same sugar exists for keyword arguments at a call site. On Ruby 3.0 and earlier the shorthand is a syntax error.
code
ruby · 20 linestitle = "Dune"
rating = 5
{title:, rating:} # => {title: "Dune", rating: 5}
{title:, rating: rating * 2} # => {title: "Dune", rating: 10}
class Review
attr_reader :title, :stars
def initialize(title, stars)
@title = title
@stars = stars
end
def to_h
{title:, stars:} # no locals here: calls the readers
end
end
Review.new("Dune", 5).to_h # => {title: "Dune", stars: 5}
# {votes:} # NameError: nothing named votesgo deeper
Recall that {x:} means {x: x} with a symbol key, and that it needs Ruby 3.1 or later.
Explain the lookup order, local variable then method then constant, the NameError when none exists, and that only symbol-style keys can omit values.
Weigh readability against hidden method calls inside classes, and check the Ruby version floor of every environment before adopting the syntax.
Decide whether a codebase standardises on the shorthand, balancing concise payload building against readers who cannot tell locals from method calls.
## What the shorthand expands to Since Ruby 3.1, a hash literal may **omit the value** after a symbol-style key. The value is fetched from the surrounding context by the key's name: ```ruby title = "Dune" rating = 5 {title:, rating:} # => {title: "Dune", rating: 5} ``` This is purely syntax: the parser rewrites `{title:}` into `{title: title}`. The resulting hash is an ordinary hash with **symbol keys**. ## How the value is found The name after the key is evaluated as if you had written the bare identifier: 1. **Local variable**: if a local variable called `title` is in scope, its value is used. 2. **Method call**: otherwise Ruby calls `title` on `self`. Inside an object with `attr_reader :title`, `{title:}` calls the reader. 3. **Constant**: a capitalised key such as `{Limit:}` refers to the constant `Limit`. 4. **Nothing found**: the literal raises `NameError` (undefined local variable or method). Because a local variable wins over a method, a local named `rating` shadows a `rating` method in the same scope, exactly as a bare `rating` would. ## Mixing forms Shorthand and explicit pairs can appear in the same literal, in any order: - `{title:, rating: rating.clamp(1, 5)}` omits one value and computes the other. - `{title:, "source" => "import"}` mixes a symbol shorthand with a string key written in full. The entries keep the order in which they are written, like any hash literal. ## Shorthand versus explicit pairs | Written | Means | Value comes from | |---|---|---| | `{title:}` | `{title: title}` | local `title`, else method `title` | | `{Limit:}` | `{Limit: Limit}` | constant `Limit` | | `{title: @title}` | as written | the instance variable, which the shorthand never reads directly | | `{title: title.strip}` | as written | any expression | The shorthand only ever produces a local-variable read, a method call or a constant read. When the value needs any transformation, write the pair out. ## Limits and pitfalls - **Symbol-style keys only.** The omission works with the `name:` form. A `"title" => ...` pair or any other `=>` key must be written with its value. - **No predicate names.** A key ending in `?` or `!`, such as `{published?:}`, cannot omit its value, because no local variable can have that name; the parser rejects it. - **Typos fail at runtime.** `{ratng:}` raises `NameError` when the line runs, not when the file loads, so an untested branch can hide one. - **Hidden method calls.** Inside a class, `{title:}` may call a method with side effects or cost; reading the literal alone does not tell you whether `title` is a local variable or a method. - **Version floor.** On Ruby 3.0 and earlier the shorthand is a syntax error, and the whole file fails to parse. Any gem or script that must run there cannot use it. ## Where it helps Building a payload from local variables is the typical case. When recording a book review in a word-frequency job: ```ruby def summary(review_id, words) total = words.size longest = words.max_by(&:length) {review_id:, total:, longest:} end ``` The literal reads as a list of the fields being returned, with no `review_id: review_id` repetition. The same sugar is available for keyword arguments when calling a method, `record(review_id:, total:)`, which follows the same lookup rules.
- If a local variable and a method are both named rating, which does {rating:} use?The local variable. The shorthand resolves the name exactly like a bare `rating` expression, and a local variable in scope shadows a method of the same name. Write `{rating: self.rating}` to call the method explicitly.
- Can you omit the value for a string key, as in {"title" =>}?No. Value omission works only with the symbol-style `title:` form, where the key's name doubles as the identifier to evaluate. A key written with `=>`, such as a string key, always needs its value.
saying these in an interview costs you the question
- {title:} sets the value of :title to nil
- {title:} creates the string key "title"
- The shorthand only reads local variables, never methods
- Value omission has worked since the Ruby 1.9 hash syntax
- A missing name makes {rating:} store rating: nil