Single quotes vs Colon --- What's the difference? (creating buttons)

I'm programming my way through the Agile Web dev book (3rd) and there's a part in there that's causing a bit of confusion (p. 146 to be exact).

You're creating a button in a partial (named _cart.html.erb) using the following code:

<%= button_to "Checkout" , :action => 'checkout' %>

However, earlier in the book, we created a button to empty the cart by:

<%= button_to "Empty cart" , :action => :empty_cart %>

both are pointing to actions defined in the store_controller.rb. Everything's the same except that checkout is in single quotes and empty_cart is preceded by a colon. Why is this?

Is it that 'checkout' is passed a variable where empty_cart is not? Or, am I having bigger brain fart than that?

Thank you in advance.

both will do the same in this case; you could swap the colons and quotes and everything would still be merry. The quotes define a string, the colon define the variable as a symbol. Symbols are a way to store a string only one time; that is, you can save memory by using symbols for strings which are used many times in your app. One restriction is, you cannot modify a symbol like you would modify a string. But for things like method names (like the checkout and empty_cart ones in your example), they are great.

hope it helps,

Maximiliano

:empty_cart is a symbol, whereas 'checkout' is a string. Your params hash will still wind up with :action => 'empty_cart' in either case, as the params hash is simply derived from parsing the query string. In this context, a string and a symbol are functionally equivalent because the symbol is converted to a string preparatory to sending the HTTP POST request.

Using symbols as a kind of named constant is idiomatic when that object is immutable. So, empty_cart won't change names during your application's duration -- you can us a symbol instead of a string, thus keeping one and only one copy of it in memory.

Use this as a background: String interning - Wikipedia

I'm not sure whether this explanation helps or confuses :slight_smile:

AlwaysCharging wrote:

Is it that 'checkout' is passed a variable where empty_cart is not? Or, am I having bigger brain fart than that?

Here is a simple example that will hopefully illustrate the difference between a symbol and a string:

$ irb a = :foo => :foo b = :foo => :foo a.equal?(b) => true c = 'foo' => "foo" d = 'foo' => "foo" c.equal?(d) => false

In this example notice that a is the same "thing" as b. But, c is NOT the same "thing" as d. In other words in all case :foo refers to the same instance (same thing) no matter how it's used. The variables c and d reference two separate instances of strings containing the same three characters ("foo").

So it's good practice to use symbols to reference "things" in your program. This is why symbols make good keys in hashes since the keys in hashes are generally used to identify things in the hash and the actual characters are not as important.

Example: attributes = { :name => "William", :occupation => "Programmer" } puts attributes[:name] => "William"

Read attributes[:name] as, "Get me the thing referenced by :name from attributes." The actual characters in the symbol :name don't really matter it could have been called :xyz and it would still mean the same thing { :xyz => "William" }; attributes(:xyz) => "William".

Now imagine what would happen if strings were used as keys:

a = { 'name' => "William", 'occupation' => "Programmer" } b = { 'name' => "Steve", 'occupation' => "Project Manager" }

In this case there would be four separate String instances to represent the same keys (same string of characters 'name' and 'occupation' but when symbols are used for keys only two symbols are created :name and :occupation. Each occurrence of each symbol, no matter how many hashes exist, are only stored in memory once. So even if a thousand hashes use the symbol :name the symbol would only take up memory once on its first usage.

So it's good practice to use symbols to reference "things" in your program. This is why symbols make good keys in hashes since the keys in hashes are generally used to identify things in the hash and the actual characters are not as important.

But, pay attention to the API for the library you are using. They don't always accept a symbol -- some expect a string. They'll do something like this:

case passed_in_argument when "foo" then ... when "bar" then ... end

If you pass in :foo for the argument it won't match.

Now that said, most of Rails runs stringify_keys on the arguments so it's a non issue. Not all the plugins do. Just something to watch out for if your symbol isn't doing what you expected it to do.

Example: attributes = { :name => "William", :occupation => "Programmer" } puts attributes[:name] => "William"

Read attributes[:name] as, "Get me the thing referenced by :name from attributes." The actual characters in the symbol :name don't really matter it could have been called :xyz and it would still mean the same thing { :xyz => "William" }; attributes(:xyz) => "William".

Now imagine what would happen if strings were used as keys:

a = { 'name' => "William", 'occupation' => "Programmer" } b = { 'name' => "Steve", 'occupation' => "Project Manager" }

In this case there would be four separate String instances to represent the same keys (same string of characters 'name' and 'occupation' but when symbols are used for keys only two symbols are created :name and :occupation. Each occurrence of each symbol, no matter how many hashes exist, are only stored in memory once. So even if a thousand hashes use the symbol :name the symbol would only take up memory once on its first usage.

I thought I'd read somewhere though that a symbol once created is never destroyed so it takes up space in RAM. A string will be destroyed once it goes out of scope. The take away being that if you create a thousand symbols but only use them once you're going to be eating up a lot of RAM. An edge case maybe as typically you'll use the symbol many times, but something to keep an eye on.

Someone please correct me if I'm wrong about the symbol/ram/usage issue.

-philip

Philip Hallstrom wrote:

As it goes with many things in life there are trade-offs, and finding the right balance is often complex. There is overhead in constantly creating and destroying objects, but keeping them around consumes memory. Also keep in mind that symbols are stored internally as integers, hence making comparisons significantly more efficient than comparing strings.

Another reason why they make good hash keys. There is a good post on symbols at has_many :through - Symbols are not pretty strings

Fred

On the other hand.

The HashWithIndifferentAccess which ActiveSupport adds to Ruby and is used extensively by Rails doesn't actually use symbols as the hash keys.

Although it allows symbols and strings to be used interchangeably as key arguments to the Hash methods. It converts symbiols to strings before actually using them as keys, rather than the other way around, which simply allows keystrokes to be saved in source code :str instead of 'str'.

The reason it does it this way is that the keys often come in the form of strings, and those strings come from outside the app, for example when url parameters get converted to keys in the params hash. Since converting strings to symbols makes a copy of the string the first time that particular string gets interned as a symbol, and those can never be garbage collected, strings rather than symbols are used as the keys.