Ruby truly is full of surprises. Until recently I was under the impression that Fixnum objects were like every other objects. A discussion emerging from one of my article made me realize that it was not the case. I then decided to understand once and for all what Fixnums were really made of.
You don’t create a Fixnum, you just use it
Not unlike symbols, you cannot create a fixnum that way : x = Fixnum.new. That just won’t work. Fixnum are immutable objects, meaning that you cannot change them once they have been created. And it is Ruby duty to create a Fixnum object, not ours. All we have to do is to use these fixnum instances and be happy.
You don’t work with a reference, you work with the object itself
When you write : x = 5, you could be tempted to think that you just created a space in memory for the variable x, and that this space is pointing to the immutable fixnum object 5. Well, it looks like this is not what’s really happening (Official doc). As I understand it, the integer value is stored directly into the variable x (more on immediate values in the update below). Moreover, no copie of a Fixnum object can be created. There may exists only one Fixnum instance for a given integer value. When ruby sees x, it scratch it’s chin for a few seconds and says : “x? Oh, he’s talking about 5!” and then it uses the Fixnum object 5. That’s why x and 5 share the same object id (x.object_id == 5.object_id). When you think about it, it’s better that way, otherwise it would have been overkill. Why having 10000 representations of the number 5 instead of only 1?
Is it useful to know?
Yes and no. After all, the only thing that really matters in our day to day programming is the fact that Fixnums are objects and that we can invoke methods on them. But if you’re like me, sometimes you just have to know how things work or you just can’t sleep well. =)
Fixnum are objects that contain immediate values
What does it means? It means that if we write x = 5, the number 5 will be stored (encoded) directly into the variable x like if it was a primitive data type (with ruby traditional objects, x would have contained an address corresponding to the object stored in the heap). So, when we write x = 5, x knows everything it needs about the number 5. In fact, x IS the object and not a reference to it.
How can you call a method like x.times if x doesn’t point to some object in memory?
What’s even more confusing is the fact that you can actually use x as a receiver and call some Fixnum methods even if the value of x doesn’t resides in the program heap. Honestly, I don’t really know how it works but I’m starting to wonder if it’s really that important. I think it’s just safe to say that the folks behind ruby really put a lot of effort to make Fixnum objects behave like every other objects. They probably wanted to hide those details as much as possible to avoid confusion. But still, Fixnums are not like every other objects.
I like the comment by RifRaf, who says :
This is done for efficiency. The object stuff is just fakery.
Fakery, yep! That’s a nice way to look at it. Fixnums are fake objects… but as ruby programmers, we can still see them as real objects without any danger of doing something wrong.