What's the difference between a string and a symbol in Ruby? - ruby

What's the difference between a string and a symbol in Ruby and when should I use one over the other?

The main difference is that multiple symbols representing a single value are identical whereas this is not true with strings. For example:
irb(main):007:0> :test.object_id
=> 83618
irb(main):008:0> :test.object_id
=> 83618
irb(main):009:0> :test.object_id
=> 83618
Those are three references to the symbol :test, which are all the same object.
irb(main):010:0> "test".object_id
=> -605770378
irb(main):011:0> "test".object_id
=> -605779298
irb(main):012:0> "test".object_id
=> -605784948
Those are three references to the string "test", but are all different objects.
This means that using symbols can potentially save a good bit of memory depending on the application. It is also faster to compare symbols for equality since they are the same object, comparing identical strings is much slower since the string values need to be compared instead of just the object ids.
As far as when to use which, I usually use strings for almost everything except things like hash keys where I really want a unique identifier, not a string.

What are the differences between Symbols and Strings?
Symbols are immutable: Their value remains constant.
Multiple uses of the same symbol have the same object ID and are the same object compared to string which will be a different object with unique object ID, everytime.
You can't call any of the String methods like split on Symbols.
From Understanding Differences Between Symbols & Strings in Ruby
If you know Chinese, you can also read 理解 Ruby Symbol.

The statement:
foo = "bar"
creates a new object in memory. If we repeat the statement:
foo = "bar"
We create another object.
To understand it more clearly please try this code in IRB:
foo = "bar"
puts "string #{foo} with object id = #{foo.object_id}"
foo = "bar"
puts "string #{foo} with object id = #{foo.object_id}"
You will get output like:
string bar with object id = 70358547221180
string bar with object id = 70358548927060
which clearly shows there are two different object for the same string. Now if you use a symbol it will create one object per symbol so:
foo = :bar
puts "symbol #{foo} with object id = #{foo.object_id}"
foo = :bar
puts "symbol #{foo} with object id = #{foo.object_id}"
shows:
symbol bar with object id = 7523228
symbol bar with object id = 7523228
which means there is only one object for :bar.
Further, Symbols are immutable and you can't call any of the String methods like upcase or split on Symbols.
Comparing Symbols are faster than comparing Strings. Symbols can be thought of as constant/immutable strings that form a unique set that are effectively converted to memory pointers on the heap. This means comparing two symbols is fast because you are just comparing two integers (memory pointers).
Strings are mutable so the memory pointer to their value on the heap can change after modification. This means comparison operations are slower because duplicates can exist that are semantically equivalent.
Use a symbol when you are sure that the value will remain constant, for example use symbols for hash keys. Use a string when you want to change the value or want to use a string method on it.

An additional difference between String and Symbol is that a String has a lot more methods on it for string manipulation, while a Symbol is a relatively lean object.
Check out the documentation for the String class and the Symbol class.

Case where symbol can be disaster. Lets say you have
params.map(&:to_sym) in your rails controller .
Now here if you are converting the data provided by the user to symbol due to some reason then it could be dangerous. If the data provided by the user is too large and as we know that symbol is not a garbage collector, you might end up exhausting your server's memory which can takedown your website.

There are two main differences between String and Symbol in Ruby.
String is mutable and Symbol is not:
Because the String is mutable, it can be change in somewhere and can lead to the result is not correct.
Symbol is immutable.
String is an Object so it needs memory allocation
puts "abc".object_id # 70322858612020
puts "abc".object_id # 70322846847380
puts "abc".object_id # 70322846815460
In the other hand, Symbol will return the same object:
puts :abc.object_id # 1147868
puts :abc.object_id # 1147868
puts :abc.object_id # 1147868
So the String will take more time to use and to compare than Symbol.
Read "The Difference Between Ruby Symbols and Strings" for more information.

The main difference is that string can have value inside a variable whereas symbol not . For example:
x = "hello"
p x => "hello"
p :x => :x

A symbol is something you use to represent names and strings. You would want to use a symbol when you may have need to use a string several times as this far easier and more productive.
And just found this via google, which may offer greater detail: Here you go

Symbols and strings are completely different this post has a little insight into the differences. As to when and where to use them, there is a pretty extensive post on this subject over on has many :through.

symbol is immutable and string is mutable.
when we perform any operation on string then it create a new object and take memory. As we perform more and more operation on string means we are consuming more and more memory.
symbol is object that are immutable mean if we perform any operation then it performs changes in original object, It will not create any object, that's why it is more profitable.
for more info, you can click here

Related

Ruby immutability of strings and symbols (What if we store them in variables)

A string is a primitive type; whenever you call the string, it has a new object id. A symbol is a referenced type; whenever you create a symbol, you create a pointer, which points to the value.
I stored symbols in variables:
var1 = :foo
var1.object_id # => 2598748
:foo.object_id # => 2598748
var2 = :foo
var2.object_id # => 2598748
var2 = "hello"
var2.object_id # => 70131755422100
How is it possible that I create a second variable var2, and it has the same object id as var1? I create a second element. Does it mean that variables are also pointers?
Both variables point to the symbol :foo. The symbol :foo is stored just once, right?
Two variables are created, so they should be in the memory, and they cannot be in the same place because they have different names. var1 and var2 need to be stored, so that I can call them later. I don't get how I can call them if they have the same object id. If someone can help me to understand this, I'd be thankful.
Ruby variables are references to objects, so when you send a method to a variable, the object it references is the context in which it is evaluated. It's probably more clear to look at the first image in the top rated answer (below the accepted answer) here.
So, to figure out what's going on, let's dig into the documentation a bit and see what happens with your code snippet.
Ruby's Symbol class documentation:
https://ruby-doc.org/core-2.5.0/Symbol.html
Symbol objects represent names and some strings inside the Ruby interpreter. They are generated using the :name and :"string" literals syntax, and by the various to_sym methods. The same Symbol object will be created for a given name or string for the duration of a program's execution, regardless of the context or meaning of that name. Thus if Fred is a constant in one context, a method in another, and a class in a third, the Symbol :Fred will be the same object in all three contexts.
Ruby's Object#object_id documentation:
https://ruby-doc.org/core-2.5.1/Object.html#method-i-object_id
Returns an integer identifier for obj.
The same number will be returned on all calls to object_id for a given object, and no two active objects will share an id.
So here's what's happening step-by-step:
# We create two variables that refer to the same object, :foo
var1 = :foo
var2 = :foo
var1.object_id = 2598748
var2.object_id = 2598748
# Evaluated as:
# var1.object_id => :foo.object_id => 2598748
# var2.object_id => :foo.object_id => 2598748
As discussed in the first link above, Ruby is pass-by-value, but every value is an Object, so your variables both evaluate to the same value. Since every symbol made of the same string ("foo" in this case) refers to the same object, and Object#object_id always returns the same id for the same object, you get the same id back.

What is the use of symbols?

I heard that two symbols with the same name create only one memory area, but two strings with the same content create two memory areas.
What is the use of symbols?
Is symbol like a variable? If so, how can I assign a value to a symbol?
If I allocate memory only once for symbols with the same name, what am I doing with the symbols?
Ok, so the misunderstanding probably stems from this:
A symbol is not a variable, it is a value. like 9 is a value that is a number.
A symbol is a value that is kinda of roughly a string... it's just not a string that you can change... and because you can't change it, we can use a shortcut -> all symbols with the same name/value are stored in the same memory-spot (to save space).
You store the symbol into a variable, or use the value somewhere - eg as the key of a hash.... this last is probably one of the most common uses of a symbol.
you make a hash that contains key-value pairs eg:
thing_attrs = {:name => "My thing", :colour => "blue", :size => 6}
thing_attrs[:colour] # 'blue'
In this has - the symbols are the keys you can use any object as a key, but symbols are good to use as they use english words, and are thus easy to understand what you're storing/fetching... much better than, say numbers. Imagine you had:
thing_attrs = {0 => "My thing", 1 => "blue", 2 => 6}
thing_attrs[1] # => "blue"
It would be annoying and hard to remember that attribute 1 is the colour... it's much nicer to give names that you can read when you're reading the code. Thus we have two options: a string, or a symbol.
There would be very little difference between the two. A string is definitely usable eg:
thing_attrs = {"name" => "My thing", "colour" => "blue", "size" => 6}
thing_attrs["colour"] # 'blue'
except that as we know... symbols use less memory. Not a lot less, but enough less that in a large program, over time, you will notice it.
So it has become a ruby-standard to use symbols instead.
A Symbol is the most basic Ruby object you can create. It's just a name and an internal ID. Symbols are useful because a given symbol name refers to the same object throughout a Ruby program. Symbols are more efficient than strings. Two strings with the same contents are two different objects, but for any given name there is only one Symbol object. This can save both time and memory.
# p039symbol.rb
# use the object_id method of class Object
# it returns an integer identifier for an object
puts "string".object_id
puts "string".object_id
puts :symbol.object_id
puts :symbol.object_id
>ruby p039symbol.rb
21066960
21066930
132178
132178
>Exit code: 0
I find it most helpful to think of symbols as strings without all the fancy string features.
Their main use is as the name of other things in your program.
For example, say you have a hash of user information.
You could do
user = { 'name' => 'Tim', 'age' => 20 }
Here the hash keys are strings.
This is fine, but it's really overkill.
Strings have lots of fancy methods like substitution and smart capitalization, and they are mutable (i.e. string objects can be changed in place).
You are never going to change hash keys, so you don't need any of that.
Thus, it's better just to use symbols
user = { :name => 'Tim', :age => 20 }
Another place symbols show up is referring to class methods.
Say I have the following class
class Foo
def initialize(bar)
#bar = bar
end
end
If I need access to the value of #bar from outside of Foo I need to add a getter method to Foo.
That would look like this
class Foo
def initialize(bar)
#bar = bar
end
def bar
bar
end
end
However, this is a pretty common thing to want to do, so ruby provides shorthand as follows
class Foo
attribute_reader :bar
def initialize(bar)
#bar = bar
end
end
This tells Foo to create for us the method we added by hand in the previous version.
As you can see, we refer to the #bar variable using a symbol.
In answer to your second question, no, a symbol is not a variable.
You can't "assign it a value."
However, as you can see, symbols can sometimes be used to refer to variables.

ruby- method_missing return no method error

I am trying to use method_missing to convert dollar to different currencies.
class Numeric
##currency={"euro"=>2, "yen"=>6}
def method_missing(method_id,*args,&block)
method_id.to_s.gsub!(/s$/,'') #get rid of s in order to handle "euros"
##currency.has_key?(method_id) ? self*##currency[method_id] : super
end
end
> 3.euro #expect to return 6
NoMethodError: undefined method 'euro' for 3:Fixnum
from (pry):24:in 'method_missing'
> 3.euros #expect to return 6
NoMethodError: undefined method 'euros' for 3:Fixnum
from (pry):24:in 'method_missing'
I guess 3.euro isn't working because :euro.to_s.gsub!(/s$/,'') returns nil. I am not sure why it returns no method error.
Thanks for any help.
When method_missing will be called, then euro will be assigned to the method_id as a symbol. But your ##currency hash holds all keys as a strings, you need to convert them as symbols.
method_id.to_s.gsub!(/s$/,'') no way will change the actual object method_id. Because method_id.to_s will give you a new string object, and #gsub! will work on this only. No way you are changing the method_id, it remains as it is. Better to use non-bang one, because it will give you the actual object if no change made, or changed object, if change is made, then you need to assign this return value to a new variable.
With your ##currency, ##currency.has_key?(method_id) evaluated as false and super class method_missing has been called. Why? As method_id didn't convert into strings, which you expected may happened.
But, if you want to respond for both like euro or euros, then
class Numeric
##currency={"euro" => 2, "yen" => 6}
def method_missing(method_id,*args,&block)
#get rid of s in order to handle "euros"
new_method_id = method_id.to_s.gsub(/s$/,'')
##currency.has_key?(new_method_id) ? self*##currency[new_method_id] : super
end
end
3.euros # => 6
3.euro # => 6
gsub! modifies a string in place and returns nil. That won't work very well with the string you're using, which is a temporary created by to_s and never stored in a variable. Also, you are not even storing the result anywhere anyway. Why should gsub! on a string be expected to modify symbols, which are immutable anyway?
Try using gsub instead, which returns the modified string and leaves the caller alone. You'll also need to store the return value.
real_method_name = method_id.to_s.gsub(/s$/, "")
Also: The reason 3.euro didn't work is because your hash uses strings as keys but method_id is passed to the method as a symbol. If you didn't have to do string manipulations (to remove s suffixes, in this case), I would have suggested to just use symbols as your hash keys. As it is, though, you need to do string operations anyway, so my answer uses strings.

Why is a string key for a hash frozen?

According to the specification, strings that are used as a key to a hash are duplicated and frozen. Other mutable objects do not seem to have such special consideration. For example, with an array key, the following is possible.
a = [0]
h = {a => :a}
h.keys.first[0] = 1
h # => {[1] => :a}
h[[1]] # => nil
h.rehash
h[[1]] # => :a
On the other hand, a similar thing cannot be done with a string key.
s = "a"
h = {s => :s}
h.keys.first.upcase! # => RuntimeError: can't modify frozen String
Why is string designed to be different from other mutable objects when it comes to a hash key? Is there any use case where this specification becomes useful? What other consequences does this specification have?
I actually have a use case where absence of such special specification about strings may be useful. That is, I read with the yaml gem a manually written YAML file that describes a hash. the keys may be strings, and I would like to allow case insensitivity in the original YAML file. When I read a file, I might get a hash like this:
h = {"foo" => :foo, "Bar" => :bar, "BAZ" => :baz}
And I want to normalize the keys to lower case to get this:
h = {"foo" => :foo, "bar" => :bar, "baz" => :baz}
by doing something like this:
h.keys.each(&:downcase!)
but that returns an error for the reason explained above.
In short it's just Ruby trying to be nice.
When a key is entered in a Hash, a special number is calculated, using the hash method of the key. The Hash object uses this number to retrieve the key. For instance, if you ask what the value of h['a'] is, the Hash calls the hash method of string 'a' and checks if it has a value stored for that number. The problem arises when someone (you) mutates the string object, so the string 'a' is now something else, let's say 'aa'. The Hash would not find a hash number for 'aa'.
The most common types of keys for hashes are strings, symbols and integers. Symbols and integers are immutable, but strings are not. Ruby tries to protect you from the confusing behaviour described above by dupping and freezing string keys. I guess it's not done for other types because there could be nasty performance side effects (think of large arrays).
Immutable keys make sense in general because their hash codes will be stable.
This is why strings are specially-converted, in this part of MRI code:
if (RHASH(hash)->ntbl->type == &identhash || rb_obj_class(key) != rb_cString) {
st_insert(RHASH(hash)->ntbl, key, val);
}
else {
st_insert2(RHASH(hash)->ntbl, key, val, copy_str_key);
}
In a nutshell, in the string-key case, st_insert2 is passed a pointer to a function that will trigger the dup and freeze.
So if we theoretically wanted to support immutable lists and immutable hashes as hash keys, then we could modify that code to something like this:
VALUE key_klass;
key_klass = rb_obj_class(key);
if (key_klass == rb_cArray || key_klass == rb_cHash) {
st_insert2(RHASH(hash)->ntbl, key, val, freeze_obj);
}
else if (key_klass == rb_cString) {
st_insert2(RHASH(hash)->ntbl, key, val, copy_str_key);
}
else {
st_insert(RHASH(hash)->ntbl, key, val);
}
Where freeze_obj would be defined as:
static st_data_t
freeze_obj(st_data_t obj)
{
return (st_data_t)rb_obj_freeze((VALUE) obj);
}
So that would solve the specific inconsistency that you observed, where the array-key was mutable. However to be really consistent, more types of objects would need to be made immutable as well.
Not all types, however. For example, there'd be no point to freezing immediate objects like Fixnum because there is effectively only one instance of Fixnum corresponding to each integer value. This is why only String needs to be special-cased this way, not Fixnum and Symbol.
Strings are a special exception simply as a matter of convenience for Ruby programmers, because strings are very often used as hash keys.
Conversely, the reason that other object types are not frozen like this, which admittedly leads to inconsistent behavior, is mostly a matter of convenience for Matz & Company to not support edge cases. In practice, comparatively few people will use a container object like an array or a hash as a hash key. So if you do so, it's up to you to freeze before insertion.
Note that this is not strictly about performance, because the act of freezing a non-immediate object simply involves flipping the FL_FREEZE bit on the basic.flags bitfield that's present on every object. That's of course a cheap operation.
Also speaking of performance, note that if you are going to use string keys, and you are in a performance-critical section of code, you might want to freeze your strings before doing the insertion. If you don't, then a dup is triggered, which is a more-expensive operation.
Update #sawa pointed out that leaving your array-key simply frozen means the original array might be unexpectedly immutable outside of the key-use context, which could also be an unpleasant surprise (although otoh it would serve you right for using an array as a hash-key, really). If you therefore surmise that dup + freeze is the way out of that, then you would in fact incur possible noticeable performance cost. On the third hand, leave it unfrozen altogether, and you get the OP's original weirdness. Weirdness all around. Another reason for Matz et al to defer these edge cases to the programmer.
See this thread on the ruby-core mailing list for an explanation (freakily, it happened to be the first mail I stumbled across when I opened up the mailing list in my mail app!).
I've no idea about the first part of your question, but hHere is a practical answer for the 2nd part:
new_hash = {}
h.each_pair do |k,v|
new_hash.merge!({k.downcase => v})
end
h.replace new_hash
There's lots of permutations of this kind of code,
Hash[ h.map{|k,v| [k.downcase, v] } ]
being another (and you're probably aware of these, but sometimes it's best to take the practical route:)
You are askin 2 different questions: theoretical and practical. Lain was the first to answer, but I would like to provide what I consider a proper, lazier solution to your practical question:
Hash.new { |hsh, key| # this block get's called only if a key is absent
downcased = key.to_s.downcase
unless downcased == key # if downcasing makes a difference
hsh[key] = hsh[downcased] if hsh.has_key? downcased # define a new hash pair
end # (otherways just return nil)
}
The block used with Hash.new constructor is only invoked for those missing keys, that are actually requested. The above solution also accepts symbols.
A very old question - but if anyone else is trying to answer the "how can I get around the hash keys are freezing strings" part of the question...
A simple trick you could do to solve the String special case is:
class MutableString < String
end
s = MutableString.new("a")
h = {s => :s}
h.keys.first.upcase! # => RuntimeError: can't modify frozen String
puts h.inspect
Doesn't work unless you are creating the keys, and unless you are then careful that it doesn't cause any problems with anything that strictly requires that the class is exactly "String"

How to understand symbols in Ruby

Despite reading "Understanding Ruby Symbols", I'm still confused by the representation of the data in memory when it comes to using symbols. If a symbol, two of them contained in different objects, exist in the same memory location, then how is it that they contain different values? I'd have expected the same memory location to contain the same value.
This a quote from the link:
Unlike strings, symbols of the same name are initialized and exist in memory only once during a session of ruby
I don't understand how it manages to differentiate the values contained in the same memory location.
Consider this example:
patient1 = { :ruby => "red" }
patient2 = { :ruby => "programming" }
patient1.each_key {|key| puts key.object_id.to_s}
3918094
patient2.each_key {|key| puts key.object_id.to_s}
3918094
patient1 and patient2 are both hashes, that's fine. :ruby however is a symbol. If we were to output the following:
patient1.each_key {|key| puts key.to_s}
Then what will be output? "red", or "programming"?
Forgetting hashes for a second, I'm thinking a symbol is a pointer to a value. The questions I have are:
Can I assign a value to a symbol?
Is a symbol just a pointer to a variable with a value in it?
If symbols are global, does that mean a symbol always points to one thing?
Consider this:
x = :sym
y = :sym
(x.__id__ == y.__id__ ) && ( :sym.__id__ == x.__id__) # => true
x = "string"
y = "string"
(x.__id__ == y.__id__ ) || ( "string".__id__ == x.__id__) # => false
So, however you create a symbol object, as long as its contents are the same, it will refer to the same object in memory. This is not a problem because a symbol is an immutable object. Strings are mutable.
(In response to the comment below)
In the original article, the value is not being stored in a symbol, it is being stored in a hash. Consider this:
hash1 = { "string" => "value"}
hash2 = { "string" => "value"}
This creates six objects in the memory -- four string objects and two hash objects.
hash1 = { :symbol => "value"}
hash2 = { :symbol => "value"}
This only creates five objects in memory -- one symbol, two strings and two hash objects.
I was able to grock symbols when I thought of it like this. A Ruby string is an object that has a bunch of methods and properties. People like to use strings for keys, and when the string is used for a key then all those extra methods aren't used. So they made symbols, which are string objects with all the functionality removed, except that which is needed for it to be a good key.
Just think of symbols as constant strings.
The symbol :ruby does not contain "red" or "programming". The symbol :ruby is just the symbol :ruby. It is your hashes, patient1 and patient2 that each contain those values, in each case pointed to by the same key.
Think about it this way: If you go into the living room on christmas morning, and see two boxes with a tag on them that say "Kezzer" on them. On has socks in it, and the other has coal. You're not going to get confused and ask how "Kezzer" can contain both socks and coal, even though it is the same name. Because the name isn't containing the (crappy) presents. It's just pointing at them. Similarly, :ruby doesn't contain the values in your hash, it just points at them.
You might be presuming that the declaration you've made defines the value of a Symbol to be something other than what it is. In fact, a Symbol is just an "internalized" String value that remains constant. It is because they are stored using a simple integer identifier that they are frequently used as that is more efficient than managing a large number of variable-length strings.
Take the case of your example:
patient1 = { :ruby => "red" }
This should be read as: "declare a variable patient1 and define it to be a Hash, and in this store the value 'red' under the key (symbol 'ruby')"
Another way of writing this is:
patient1 = Hash.new
patient1[:ruby] = 'red'
puts patient1[:ruby]
# 'red'
As you are making an assignment it is hardly surprising that the result you get back is identical to what you assigned it with in the first place.
The Symbol concept can be a little confusing as it's not a feature of most other languages.
Each String object is distinct even if the values are identical:
[ "foo", "foo", "foo", "bar", "bar", "bar" ].each do |v|
puts v.inspect + ' ' + v.object_id.to_s
end
# "foo" 2148099960
# "foo" 2148099940
# "foo" 2148099920
# "bar" 2148099900
# "bar" 2148099880
# "bar" 2148099860
Every Symbol with the same value refers to the same object:
[ :foo, :foo, :foo, :bar, :bar, :bar ].each do |v|
puts v.inspect + ' ' + v.object_id.to_s
end
# :foo 228508
# :foo 228508
# :foo 228508
# :bar 228668
# :bar 228668
# :bar 228668
Converting strings to symbols maps identical values to the same unique Symbol:
[ "foo", "foo", "foo", "bar", "bar", "bar" ].each do |v|
v = v.to_sym
puts v.inspect + ' ' + v.object_id.to_s
end
# :foo 228508
# :foo 228508
# :foo 228508
# :bar 228668
# :bar 228668
# :bar 228668
Likewise, converting from Symbol to String creates a distinct string every time:
[ :foo, :foo, :foo, :bar, :bar, :bar ].each do |v|
v = v.to_s
puts v.inspect + ' ' + v.object_id.to_s
end
# "foo" 2148097820
# "foo" 2148097700
# "foo" 2148097580
# "bar" 2148097460
# "bar" 2148097340
# "bar" 2148097220
You can think of Symbol values as being drawn from an internal Hash table and you can see all values that have been encoded to Symbols using a simple method call:
Symbol.all_values
# => [:RUBY_PATCHLEVEL, :vi_editing_mode, :Separator, :TkLSHFT, :one?, :setuid?, :auto_indent_mode, :setregid, :back, :Fail, :RET, :member?, :TkOp, :AP_NAME, :readbyte, :suspend_context, :oct, :store, :WNOHANG, :#seek, :autoload, :rest, :IN_INPUT, :close_read, :type, :filename_quote_characters=, ...
As you define new symbols either by the colon-notation or by using .to_sym this table will grow.
Symbols are not pointers. They do not contain values. Symbols simply are. :ruby is the symbol :ruby and that's all there is to it. It doesn't contain a value, it doesn't do anything, it just exists as the symbol :ruby. The symbol :ruby is a value just like the number 1 is. It doesn't point to another value any more than the number 1 does.
patient1.each_key {|key| puts key.to_s}
Then what will be output? "red", or
"programming"?
Neither, it will output "ruby".
You're confusing symbols and hashes. They aren't related, but they're useful together. The symbol in question is :ruby; it has nothing to do with the values in the hash, and it's internal integer representation will always be the same, and it's "value" (when converted to a string) will always be "ruby".
In short
Symbols solve the problem of creating human readable, immutable representations that also have the benefit of being simpler for the runtime to lookup than strings. Think of it like a name or label that can be reused.
Why :red is better than "red"
In dynamic object oriented languages you create complex, nested data structures with readable references. The hash is a common use case where you map values to unique keys — unique, at least, to each instance. You can't have more than one "red" key per hash.
However it would be more processor efficient to use a numeric index instead of string keys. So symbols were introduced as a compromise between speed and readability. Symbols resolve much easier than the equivalent string. By being human readable and easy for the runtime to resolve symbols are an ideal addition to a dynamic language.
Benefits
Since symbols are immutable they can be shared across the runtime. If two hash instances have a common lexicographic or semantic need for a red item the symbol :red would use roughly half the memory that the string "red" would have required for two hashes.
Since :red always resolves back to the same location in memory it can be reused across a hundred hash instances with almost no increase in memory, whereas using "red" will add a memory cost since each hash instance would need to store the mutable string upon creation.
Not sure how Ruby actually implements symbols/string but clearly a symbol offers less implementation overhead in the runtime since it's a fixed representation. Plus symbols takes one less character to type than a quoted string and less typing is the eternal pursuit of true Rubyists.
Summary
With a symbol like :red you get the readability of string representation with less overhead due to the cost of string comparison operations and the need to store each string instance in memory.
I would recommend reading the Wikipedia article on hash tables - I think it will help you get a sense of what {:ruby => "red"} really means.
Another exercise that might help your understanding of the situation: consider {1 => "red"}. Semantically, this doesn't mean "set the value of 1 to "red"", which is impossible in Ruby. Rather, it means "create a Hash object, and store the value "red" for the key 1.
patient1 = { :ruby => "red" }
patient2 = { :ruby => "programming" }
patient1.each_key {|key| puts key.object_id.to_s}
3918094
patient2.each_key {|key| puts key.object_id.to_s}
3918094
patient1 and patient2 are both hashes, that's fine. :ruby however is a symbol. If we were to output the following:
patient1.each_key {|key| puts key.to_s}
Then what will be output? "red", or "programming"?
Neither, of course. The output will be ruby. Which, BTW, you could have found out in less time than it took you to type the question, by simply typing it into IRB instead.
Why would it be red or programming? Symbols always evaluate to themselves. The value of the symbol :ruby is the symbol :ruby itself and the string representation of the symbol :ruby is the string value "ruby".
[BTW: puts always converts its arguments to strings, anyway. There's no need to call to_s on it.]
I'm new to Ruby, but I think (hope?) this is a simple way to look at it...
A symbol is not a variable or a constant. It doesn't stand in for, or point to, a value. A symbol IS a value.
All it is, is a string without the object overhead. The text and only the text.
So, this:
"hellobuddy"
Is the same as this:
:hellobuddy
Except you can't do, for example, :hellobuddy.upcase. It's the string value and ONLY the string value.
Likewise, this:
greeting =>"hellobuddy"
Is the same as this:
greeting => :hellobuddy
But, again, without the string object overhead.
One easy way to wrap your head around this is to think, "what if I were using a string rather than a symbol?
patient1 = { "ruby" => "red" }
patient2 = { "ruby" => "programming" }
It isn't confusing at all, right?
You're using "ruby" as a key in a hash.
"ruby" is a string literal, so that is the value. The memory address, or pointer, is not available to you.
Every time you invoke "ruby", you are creating a new instance of it, that is, creating a new memory cell containing the same value - "ruby".
The hash then goes "what's my key value? Oh it's "ruby". Then maps that value to "red" or "programming".
In other words, :ruby doesn't dereference to "red" or "programming".
The hash maps :ruby to "red" or "programming".
Compare that to if we use symbols
patient1 = { :ruby => "red" }
patient2 = { :ruby => "programming" }
The value of :ruby is also "ruby", effectively.
Why? Because symbols are essentially string constants.
Constants don't have multiple instances. It's the same memory address. And a memory address has a certain value, once dereferenced. For symbols, the pointer name is the symbol, and the dereferenced value is a string, which matches the symbol name, in this case, "ruby".
When in a hash, you are not using the symbol, the pointer, but the deferenced value. You're not using :ruby, but "ruby".
The hash then looks up for key "ruby", the value is "red" or "programming", depending on how you defined the hash.
The paradigm shift and take-home concept is that a symbol's value is a completely separate concept from a value mapped to by a hash, given a key of that hash.

Resources