Reason #167 • June 16th, 2026

Brackets are methods

In Ruby, executing array[i] or hash[key] is actually syntactic sugar for array.[](i) and hash.[](key). In the same way, array[i] = value is syntactic sugar for array.[]=(i, value). This means that we can easily define our own [] and []= methods on custom classes to provide similar behavior.

Let's use it to implement a case-insensitive hash to handle HTTP headers:

Ruby
class Headers
  def initialize(initial_headers = {})
    @data = initial_headers.transform_keys(&:downcase)
  end

  def [](key)
    @data[key.downcase]
  end

  def []=(key, value)
    @data[key.downcase] = value
  end
end

headers = Headers.new("Content-Type" => "application/json")
headers["Content-Type"] # => "application/json"
headers["content-type"] # => "application/json"

headers["Accept"] = "text/html"
headers["accept"] # => "text/html"

headers["authorization"] = "Bearer token"
headers["Authorization"] # => "Bearer token"
    

Of course, we can also define brackets as class methods, a pattern commonly used to provide a more concise and "literal-like" syntax for constructing objects:

Ruby
class Headers
  def self.[](initial_headers = {})
    new(initial_headers)
  end
end

headers = Headers["Content-Type" => "application/json"]
headers["Content-Type"] # => "application/json"
headers["content-type"] # => "application/json"
    

Concise, straightforward and absolutely lovely ❤️

History

Bracket access as method dispatch has been part of Ruby since its inception.

Reason #168 ?