Object Ids in Ruby
In my effort to master Ruby this year, I started this morning working through Ruby Koans. I just completed these tests and was intrigued by the comment in the second koan:
def test_some_system_objects_always_have_the_same_id
assert_equal 0, false.object_id
assert_equal 2, true.object_id
assert_equal 4, nil.object_id
end
def test_small_integers_have_fixed_ids
assert_equal 1, 0.object_id
assert_equal 3, 1.object_id
assert_equal 5, 2.object_id
assert_equal 201, 100.object_id
# THINK ABOUT IT:
# What pattern do the object IDs for small integers follow?
end
To put it another way:
>> (0..50).each { |i| print i.object_id, ' ' }
What would you expect to see as output? You can find the answer in Fixed Object Id for System Objects and Small Integers in Ruby.
But why does this happen? I did a little digging and found these 2006 articles by Caleb Tennis on The Ruby VALUE and Ruby Values and object_ids. In the first article he points out that:
The first point of interest is the VALUE - Ruby’s internal representation of its objects. In the general sense, a VALUE is just a C pointer to a Ruby object data type. We use VALUEs in the C code like we would use objects in the Ruby code.
…for performance purposes, Ruby doesn’t use the VALUE as a pointer in every instance. For Fixnums, Ruby stores the number value directly in the VALUE itself. That keeps us from having to keep a lookup table of every possible Fixnum in the system.
There is also a good Stack Overflow answer to this question of how object assignment works in Ruby:
In MRI the
object_id
of an object is the same as theVALUE
that represents the object on the C level. For most kinds of objects thisVALUE
is a pointer to a location in memory where the actual object data is stored. Obviously this will be different during multiple runs because it only depends on where the system decided to allocate the memory, not on any property of the object itself.
However for performance reasons
true
,false
,nil
andFixnum`s are handled specially. For these objects there isn’t actually a struct with the object’s data in memory. All of the object’s data is encoded in the `VALUE
itself. As you already figured out the values forfalse
,true
,nil
and anyFixnum
i
, are0
,2
,4
andi*2+1
respectively.
The reason that this works is that on any systems that MRI runs on,
0
,2
,4
andi*2+1
are never valid addresses for an object on the heap, so there’s no overlap with pointers to object data.
I highly recommend using a koan-based approach to learning the details of a new language. There’s a good list of links for various languages in "Koan-a-Copia!" article by Nola Stowe.