Chapter 11. Symbols

image with no caption

Many newcomers to Ruby are confused by symbols. A symbol is an identifier whose first character is a colon (:), so :this is a symbol and so is :that. Symbols are, in fact, not at all complicated—and, in certain circumstances, they may be extremely useful, as you will see shortly.

Let’s first be clear about what a symbol is not: It is not a string, it is not a constant, and it is not a variable. A symbol is, quite simply, an identifier with no intrinsic meaning other than its own name. Whereas you might assign a value to a variable like this . . .

name = "Fred"

you would not assign a value to a symbol:

:name = "Fred"    # Error!

The value of a symbol is itself. So, the value of a symbol called :nameis :name.

Note

For a more technical account of what a symbol is, refer to Digging Deeper in Digging Deeper.

You have, of course, used symbols before. In Chapter 2, for instance, you created attribute readers and writers by passing symbols to the attr_reader and attr_writer methods, like this:

attr_reader( :description )
attr_writer( :description )

You may recall that the previous code causes Ruby to create a @description instance variable plus a pair of getter (reader) and setter (writer) methods called description. Ruby takes the value of a symbol literally. The attr_reader and attr_writer methods create, from that name, variables and methods with matching names.

Symbols and Strings

It is a common misconception that a symbol is a type of string. After all, isn’t the symbol :hello pretty similar to the string "hello"? In fact, symbols are quite unlike strings. For one thing, each string is different—so, as far as Ruby is concerned, "hello", "hello", and "hello" are three separate objects with three separate object_ids.

symbol_ids.rb

# These 3 strings have 3 different object_ids
puts( "hello".object_id ) #=> 16589436
puts( "hello".object_id ) #=> 16589388
puts( "hello".object_id ) #=> 16589340

But a symbol is unique, so :hello, :hello, and :hello all refer to the same object with the same object_id.

# These 3 symbols have the same object_id
puts( :hello.object_id ) #=> 208712
puts( :hello.object_id ) #=> 208712
puts( :hello.object_id ) #=> 208712

In this respect, a symbol has more in common with an integer than with a string. Each occurrence of a given integer value, you may recall, refers to the same object, so 10, 10, and 10 may be considered to be the same object, and they have the same object_id. Remember that the actual IDs assigned to objects will change each time you run a program. The number itself is not significant. The important thing to note is that each separate object always has a unique ID, so when an ID is repeated, it indicates repeated references to the same object.

ints_and_symbols.rb

# These three symbols have the same object_id
puts( :ten.object_id )  #=> 20712
puts( :ten.object_id )  #=> 20712
puts( :ten.object_id )  #=> 20712

# These three integers have the same object_id
puts( 10.object_id )    #=> 21
puts( 10.object_id )    #=> 21
puts( 10.object_id )    #=> 21

You can also test for equality using the equal? method:

symbols_strings.rb

puts( :helloworld.equal?( :helloworld ) )     #=> true
puts( "helloworld".equal?( "helloworld" ) )   #=> false
puts( 1.equal?( 1 ) )                         #=> true

Being unique, a symbol provides an unambiguous identifier. You can pass symbols as arguments to methods, like this:

amethod( :deletefiles )

A method might contain code to test the value of the incoming argument:

symbols_1.rb

def amethod( doThis )
    if (doThis == :deletefiles) then
       puts( 'Now deleting files...')
    elsif (doThis == :formatdisk) then
       puts( 'Now formatting disk...')
    else
        puts( "Sorry, command not understood." )
    end
end

Symbols can also be used in case statements where they provide both the readability of strings and the uniqueness of integers:

case doThis
    when :deletefiles then puts( 'Now deleting files...')
    when :formatdisk then puts( 'Now formatting disk...')
    else  puts( "Sorry, command not understood." )
end

The scope in which a symbol is declared does not affect its uniqueness. Consider the following:

symbol_ref.rb

module One
     class Fred
     end
     $f1 = :Fred
end

module Two
     Fred = 1
     $f2 = :Fred
end

def Fred()
end

$f3 = :Fred

Here, the variables $f1, $f2, and $f3 are assigned the symbol :Fred in three different scopes: module One, module Two, and the “main” scope. Variables starting with $ are global, so once created, they can be referenced anywhere. I’ll have more to say on modules in Chapter 12. For now, just think of them as “namespaces” that define different scopes. And yet each variable refers to the same symbol, :Fred, and has the same object_id.

# All three display the same id!
puts( $f1.object_id )  #=> 208868
puts( $f2.object_id )  #=> 208868
puts( $f3.object_id )  #=> 208868

Even so, the “meaning” of the symbol changes according to its scope. In module One, :Fred refers to the class Fred; in module Two, it refers to the constant Fred = 1; and in the main scope, it refers to the method Fred.

A rewritten version of the previous program demonstrates this:

symbol_ref2.rb

module One
    class Fred
    end
    $f1 = :Fred
    def self.evalFred( aSymbol )
        puts( eval( aSymbol.id2name ) )
    end
end

module Two
    Fred = 1
    $f2 = :Fred
    def self.evalFred( aSymbol )
        puts( eval( aSymbol.id2name ) )
    end
end

def Fred()
    puts( "hello from the Fred method" )
end

$f3 = :Fred

First I access the evalFred method inside the module named One using two colons (::), which is the Ruby “scope resolution operator.” I then pass $f1 to that method:

One::evalFred( $f1 )

In this context, Fred is the name of a class defined inside module One, so when the :Fred symbol is evaluated, the module and class names are displayed:

One::Fred

Next I pass $f2 to the evalFred method of module Two:

Two::evalFred( $f2 )

In this context, Fred is the name of a constant that is assigned the integer 1, so that is what is displayed: 1. And finally, I call a special method called simply method. This is a method of Object. It tries to find a method with the same name as the symbol passed to it as an argument and, if found, returns that method as an object that can then be called:

method($f3).call

The Fred method exists in the main scope, and when called, its output is this string:

"hello from the Fred method"

Naturally, since the variables $f1, $f2, and $f3 reference the same symbol, it doesn’t matter which variable you use at any given point. Any variable to which a symbol is assigned, or, indeed, the symbol itself, will produce the same results. The following are equivalent:

One::evalFred( $f1 )    #=> One::Fred
Two::evalFred( $f2 )    #=> 1
method($f3).call        #=> hello from the Fred method

One::evalFred( $f3 )    #=> One::Fred
Two::evalFred( $f1 )    #=> 1
method($f2).call        #=> hello from the Fred method

One::evalFred( :Fred )  #=> One::Fred
Two::evalFred( :Fred )  #=> 1
method(:Fred).call      #=> hello from the Fred method
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.31.163