In the last entry, I talked about Ruby VALUEs and what they mean. A few readers brought up some good points - namely that object_id’s and VALUEs look very similiar:
If we go inside Ruby for a second, here’s how the object_id is calculated:
VALUE
rb_obj_id(VALUE obj)
{
if (SPECIAL_CONST_P(obj)) {
return LONG2NUM((long)obj);
}
return (VALUE)((long)obj|FIXNUM_FLAG);
}
You can trace back the SPECIAL_CONST_P macro to point to a few other macros which point to a few other macros, etc.
What ends up happening, in general, is that the VALUE and the object_id are the same.
irb(main):001:0> "some_string".object_id => 1136556 irb(main):002:0> nil.object_id => 4 irb(main):003:0> false.object_id => 0 irb(main):004:0> true.object_id => 2 irb(main):005:0> 5.object_id => 11
Why does 5 have an object_id of 11? Well, don’t forget that Fixnums are stored in the upper 31 bits, which means a shift left of one. We also use the lowest bit to mark that it is a Fixnum.
Thus 0×0101 (5) becomes 0×1011 (11).
If we look back at our string object from above, its VALUE (1136556) correlates to 0b1000101010111110101100. Note that the lower two bits are 0 - they have to be if they represent at least a 4 byte aligned memory address (see previous entry for why).
What about symbols?
irb(main):002:0> :foo.object_id => 3895566 irb(main):004:0> :foo.to_i => 15217
So :foo’s object_id is 0b1110110111000100001110 and its to_i gives 0b11101101110001. First notice that the object_id has a 1 in the 2nd lowest bit. This again denotes a special type. Also note that there’s something similiar between the object_id and the to_i value - in fact, if you chop off the right most 8 bits from the object_id, you get the same number as to_i.
This may come into play later.

And deeper down the rabbit hole we go! This is a very interesting and insightful series. Cant wait for the next installment.
Thanks!
What a clever scheme! I am very impressed. Do you know if this is something Matz came up with himself, or is this a well-known technique ?
Anyways, thanks for the cool post
It's a very common technique.
Lisp implementations have been tagging integers/pointers "forever".