Накратко
целите числа са вероятно най-често използваният "вид" обекти в повечето програми.
Такива детайли са специфични за имплементацията, така че тук се има предвид главно MRI(референтната имплементация, която почти всички ползваме)
В този смисъл, интернирането им спестява
- памет (не се създава отделна C структура за всяко число)
- време (спестяват се проследявания на pointer-и, защото стойността се взима директно)
Имплементационни детайли и C магия
Начинът, по който това работи в MRI е сравнително прост.
Най-общо казано, на всеки Ruby обект съответства стойност от C типа VALUE
.
Най-елементарното решение в една имплементация би било тази стойност да е просто pointer към C структура, съдържаща данните на обекта. В MRI това е почти така, но не и за няколко специални типа стойности.
VALUE
e дефинирано като typedef uintptr_t VALUE
, тоест неотрицателно цяло число, което е със size, достатъчен за да съдържа адреса на pointer. Това число е точно #object_id
на един обект в Руби, така че лесно може да се поиграе и да се разгледа в irb
.
Всеки път, когато MRI се опита да интерпретира VALUE стойност, то проверява вътрешния и интерпретатор-специфичен клас, който е закодиран чрез определени битове в това число. Специфично, Fixnum
обектите са точно тези, чиито последен бит е 1. Когато такъв обект бъде засечен, още един бит кодира знака, и останалите 30 на 32-битова / 62 на 64-битова архитектура съдържат директно стойността на числото.
Именно затова всяко x
с клас Fixnum
x.object_id == 2 * x + 1
По подобен начин се интернират true
, false
, nil
и символите. За сравнение, при обикновените обекти, VALUE
се използва като адрес на структура с полета, методи и т.н. Пример:
struct RClass {
struct RBasic basic;
VALUE super;
rb_classext_t *ptr;
struct method_table_wrapper *m_tbl_wrapper;
};