In Ruby, what arguments and block does a bare super pass to the overridden method, and how do super() and super(a) differ from it?
answer
- parentheses change everything
- current values, not the caller's
- super() sends zero arguments
- the block tags along regardless
- &nil switches the block off
basics
~20 sA bare super re-sends all of the current method's parameters, using their current values, plus its block. super() sends no arguments and super(a) sends exactly a; both still pass the block unless you pass &nil or another block.
solid answer
~40 sA bare `super` (no parentheses, no arguments) is special: Ruby re-sends every parameter of the current method (positional, optional, splat and keyword) using the variables' **current** values, so reassigning `mode` before `super` sends the new value. `super()` calls the parent with zero arguments, and `super(a, b)` with exactly those. In all three forms the block the current method received is passed along implicitly; `super(a, &nil)` suppresses it and `super(a, &other)` replaces it. The practical trap is a signature mismatch: an override that takes more parameters than the parent and calls bare `super` raises `ArgumentError`, so it has to pick the parent's arguments explicitly.
code
ruby · 14 linesclass Vehicle
def initialize(name)
@name = name
end
end
class ElectricCar < Vehicle
def initialize(name, battery_kwh)
super(name) # bare super would send 2 arguments: ArgumentError
@battery_kwh = battery_kwh
end
end
ElectricCar.new("Volt", 60)go deeper
Know that super with no parentheses forwards the same arguments and that super() forwards none; both are called from inside an overriding method.
Explain that bare super reads the parameters' current values, that the block is passed by every form, and that &nil is the only way to withhold it.
Diagnose the ArgumentError from a subclass initialize that adds parameters and calls bare super, and the RuntimeError from bare super inside define_method.
Argue for a team convention: bare super only where signatures match, explicit arguments everywhere else, so signature changes in a base class fail loudly and locally.
## Three spellings, three argument lists Inside an overriding method, `super` calls the method of the same name further up the class chain. What it sends depends on the spelling: | Spelling | Arguments sent | Block sent | |---|---|---| | `super` | all of the current method's parameters, current values | the current block | | `super()` | none | the current block | | `super(a, b)` | exactly `a` and `b` | the current block | | `super(a, &nil)` | exactly `a` | none | | `super(a, &blk)` | exactly `a` | `blk` | The bare form is sometimes called **zsuper** (zero-argument super). It is the only form whose argument list is not written at the call site. ## Bare super reads the parameters now, not at entry Zsuper does not replay what the caller originally passed. It reads the method's **parameter variables** at the moment `super` runs: - a reassigned positional parameter sends its new value; - an optional parameter that was not passed sends the default that was filled in; - a splat such as `*args` sends its current contents, even if you replaced the array; - keyword parameters are sent as **keywords**, again with their current values. ```ruby class Vehicle def drive(mode = :eco) [mode, block_given?] end end class ElectricCar < Vehicle def drive(mode) mode = :sport [super, super(), super(mode, &nil)] end end ElectricCar.new.drive(:boost) { } # => [[:sport, true], [:eco, true], [:sport, false]] ``` The bare `super` sent `:sport`, the reassigned value; `super()` sent nothing, so the parent used its default `:eco`; and `super(mode, &nil)` sent `:sport` without the block. ## Keyword parameters Keyword parameters follow the same rule. An override declared `def drive(mode:, **opts)` that calls bare `super` sends `mode:` and the contents of `opts` as **keyword arguments**, using their current values, so a reassigned `mode` reaches the parent as the new value. An explicit call has to spell them out: `super(mode: mode, **opts)`, or with hash shorthand `super(mode:, **opts)`. Leaving `**opts` out of an explicit call is a quiet way to lose options the parent expected, which is one reason teams keep matching signatures on bare `super`. ## The block always tags along A very common misconception is that the parentheses in `super()` also mean "no block". They do not. Every form of `super` passes the current method's block implicitly, so a parent that calls `yield` or checks `block_given?` still sees it. There are only two ways to change that: 1. `super(args, &nil)` passes **no** block. 2. `super(args) { ... }` or `super(args, &other)` passes a **different** block. ## Where bare super breaks Because zsuper sends the current method's parameters, it assumes the parent accepts them: - **Signature mismatch.** If `ElectricCar#initialize(name, battery_kwh)` calls bare `super` and `Vehicle#initialize(name)` takes one argument, Ruby raises `ArgumentError (given 2, expected 1)`. Write `super(name)` instead. - **Parent takes nothing.** When the parent is `Object`, the `initialize` it inherits from `BasicObject` accepts no arguments, so bare `super` from an `initialize(name)` raises `ArgumentError` too; `super()` is correct there. - **Methods made by `define_method`.** Implicit argument passing is not supported inside them; bare `super` raises `RuntimeError` and asks you to specify all arguments explicitly. The reverse mistake also happens: writing `super()` when you meant to forward everything silently drops arguments and lets the parent fall back to its defaults, as the `:eco` result above shows. ## Rules of thumb 1. Use bare `super` when the override has **the same signature** as the parent and should pass everything through. 2. Use `super(explicit, args)` whenever the signatures differ, which is the normal case for `initialize` in a subclass that adds fields. 3. Use `super()` only when the parent takes no arguments, and remember that the block still goes. 4. Add `&nil` only when the parent must not see the caller's block.
- What happens if you call bare super inside a method created with define_method?It raises `RuntimeError` with the message that implicit argument passing of super from a method defined by `define_method()` is not supported and that you must specify all arguments explicitly. Write `super(mode)` or `super(*args, **opts)` instead; the explicit forms work there.
- Why does a parent method that yields still work when the override calls super() with empty parentheses?Because the parentheses only control arguments. The current method's block is passed implicitly by every form of `super`, so the parent's `yield` finds it and `block_given?` returns `true`. To hide the block, write `super(&nil)`.
- Does bare super work in a method whose parameters are anonymous, such as def drive(*) or def drive(...)?Yes. Bare `super` re-sends anonymous splats as well. Inside a method declared with `(...)` you can also forward everything explicitly with `super(...)`, which reads the same but makes the forwarding visible.
saying these in an interview costs you the question
- super and super() are the same call; the parentheses are only style.
- Bare super sends the caller's original arguments, ignoring any reassignment in the method.
- super() passes no block, so a parent that yields raises LocalJumpError.
- super(a) sends a plus the rest of the current method's arguments.
- Bare super quietly drops any arguments the parent does not accept.