Last week, I discovered an oddity about Ruby’s literal array syntax in conjunction with hashes. The following (simplified) test is syntactically valid Ruby code:

describe do let( :h ) { { "foo" => "bar" } } let( :a ) { [h] } it { a.should =~ [ "foo" => "bar" ] } end

Do you see what’s strange about the syntax? After running the test, I was about to add a second expectation and noticed the missing curly braces in line number 4. As it turns out, the following statement evaluates to true :

2.0.0-p247 :001 > [{ "foo" => "bar" }] == ["foo" => "bar"] => true

For me, this was a surprise, since I would have expected some kind a syntax error (maybe unexpected token '=>' ). Further experiments revealed the following:

2.0.0-p247 :002 > [123, { "foo" => "bar" }] == [123, "foo" => "bar"] => true 2.0.0-p247 :003 > [{ "foo" => "bar" }, 123] == ["foo" => "bar", 123] SyntaxError: (irb):3: syntax error, unexpected ']', expecting => from /Users/cs/.rvm/rubies/ruby-2.0.0-p247/bin/irb:16:in `<main>'

Literal array definitions, seem to behave exactly like ordinary method invocations: The last argument is interpreted as a hash – even without the curly braces. In the Rails community, this behaviour is widely known as the options hash.

Now, every Ruby method call must have a receiver. If no explicit receiver has been specified, the implicit receiver is self . This suggets, that there should be a method [] defined on self . Let’s check:

2.0.0-p247 :004 > self.method(:[]) NameError: undefined method `[]' for class `Object' from (irb):8:in `method' from (irb):8 from /Users/cs/.rvm/rubies/ruby-2.0.0-p247/bin/irb:16:in `<main>'

Strangely, [] is not defined. Hmmm.

By consulting the standard library documentation, I disovered the method Array.[] . Except for the explicit receiver, that class method seems to be functionally equivalent to the literal syntax:

2.0.0-p247 :004 > Array[] => [] 2.0.0-p247 :005 > Array[123] => [123] 2.0.0-p247 :006 > Array[123, "bar" => "foo"] => [123, {"bar"=>"foo"}]

Just out of curiousity, it would be interesting to find out wether Array.[] gets called when instantiating a new array with literal syntax. Luckily, Ruby’s meta-programming features make it easy to rewrite existing methods:

2.0.0-p247 :007 > class <<Array 2.0.0-p247 :008?> alias_method :original_square_brackets, :[] 2.0.0-p247 :009?> def [](*args) 2.0.0-p247 :010?> puts "Array.[] has been called!" 2.0.0-p247 :011?> original_square_brackets(*args) 2.0.0-p247 :012?> end 2.0.0-p247 :013?> end => nil 2.0.0-p247 :014 > Array[1, 2, 3] Array.[] has been called! => [1, 2, 3] 2.0.0-p247 :015 > [1, 2, 3] => [1, 2, 3]

Line number 9 clearly demonstrates that Array.[] has been rewritten as intended. However, nothing has been printed during instantiation of the literal array in line number 12.

Conclusion

Even though, [] is behaving exactly like Array.[] (which is an ordinary method), it’s not possible to change [] . In comparision, Array.[] can be overwritten easily. My guess is, that the interpreter has been optimized to directly use the native implementation of [] and skip the usual method lookup process. Magic ;)