Why is :key.hash != 'key'.hash in Ruby?

I'm learning Ruby right now for the Rhodes mobile application framework and came across this problem: Rhodes' HTTP client parses JSON responses into Ruby data structures, e.g.

puts @params # prints {"body"=>{"results"=>[]}}

Since the key "body" is a string here, my first attempt @params[:body] failed (is nil) and instead it must be @params['body']. I find this most unfortunate.

Can somebody explain the rationale why strings and symbols have different hashes, i.e. :body.hash != 'body'.hash in this case?


Symbols and strings serve two different purposes.

Strings are your good old familiar friends: mutable and garbage-collectable. Every time you use a string literal or #to_s method, a new string is created. You use strings to build HTML markup, output text to screen and whatnot.

Symbols, on the other hand, are different. Each symbol exists only in one instance and it exists always (i.e, it is not garbage-collected). Because of that you should make new symbols very carefully (String#to_sym and :'' literal). These properties make them a good candidate for naming things. For example, it's idiomatic to use symbols in macros like attr_reader :foo.

If you got your hash from an external source (you deserialized a JSON response, for example) and you want to use symbols to access its elements, then you can either use HashWithIndifferentAccess (as others pointed out), or call helper methods from ActiveSupport:

require 'active_support/core_ext'

h = {"body"=>{"results"=>[]}}
h.symbolize_keys # => {:body=>{"results"=>[]}}
h.stringify_keys # => {"body"=>{"results"=>[]}}

Note that it'll only touch top level and will not go into child hashes.

Symbols and Strings are never ==:

:foo == 'foo'  # => false

That's a (very reasonable) design decision. After all, they have different classes, methods, one is mutable the other isn't, etc...

Because of that, it is mandatory that they are never eql?:

:foo.eql? 'foo'  # => false

Two objects that are not eql? typically don't have the same hash, but even if they did, the Hash lookup uses hash and then eql?. So your question really was "why are symbols and strings not eql?".

Rails uses HashWithIndifferentAccess that accesses indifferently with strings or symbols.

In Rails, the params hash is actually a HashWithIndifferentAccess rather than a standard ruby Hash object. This allows you to use either strings like 'action' or symbols like :action to access the contents.

You will get the same results regardless of what you use, but keep in mind this only works on HashWithIndifferentAccess objects.

Copied from : Params hash keys as symbols vs strings

Need Your Help

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.