Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do Ruby variables store value or address?

Tags:

ruby

Since in Ruby everything is an object, do Ruby variables store value or address of immediate types (read primitives)? In contrast to C that stores values in variables, if they are primitives.

like image 534
Humming Avatar asked Dec 29 '16 20:12

Humming


People also ask

Does Ruby pass by reference or value?

Pass-reference-by-valueRuby is pass-by-value, but the values it passes are references. A function receives a reference to (and will access) the same object in memory as used by the caller.

How do variables work in Ruby?

Variables in Ruby can contain different values and different types of values over time. The term variable comes from the fact that variables, unlike constants, can take different values over time. In the example, there is a variable called i . First it is assigned a value 5, later a different value 7.

What does variable mean in Ruby?

What is a variable? A variable is a name that Ruby associates with a particular object. For example: city = "Toronto" Here Ruby associates the string "Toronto" with the name (variable) city. Think of it as Ruby making two tables.

Can I store address in variable?

Just like a string variable can't store a numerical value, similarly just a normal variable can't store an address. Everything is meant for some purpose. You can't store a double value into an integer variable.


2 Answers

NB, all of the following is for the default Ruby, which internally uses YARV aka "Yet another Ruby VM," other rubies like JRuby may use different internal representations…

Good question.

Ruby uses tag pointers for integers, and everything else is stored as a reference to an object.

How do they work? One bit in the pointer is used as tag, if that bit is set the rest of the pointer is interpreted as an integer and otherwise as an address.

This works because some bits in a pointer are not used. The bottom most bits of a memory address are usually not used. Most systems only allow address of aligned memory addresses, for example aligned to 4 bytes and so 2 bit become available to be used as tag. And then if that tag is set, the other 31 bit of the pointer are interpreted as integer.

You can see this when you look at the object_id of integers

20.to_s(2) # => "10100"
20.object_id.to_s(2) # => "101001"

On some systems two tag bits are used and then floating-point numbers are represented using the other tag bit. And there are some special objects like nil, true, false that are represented with reserved numbers that are unlikely to be valid memory addresses. Symbols are also represented as tagged integers internally but with a different bitmask than actual integers.

All other values are represented as pointers.

Fun fact, you can inspect all of that yourself using ObjectSpace class.

(0..100).each { |n| p([n, ObjectSpace._id2ref(n)]) rescue nil }

On my system this prints

[0, false]
[1, 0]
[2, 2.0]
[3, 1]
[5, 2]
[6, -2.0]
[7, 3]
[8, nil]
[9, 4]
[10, 2.0000000000000004]
[11, 5]
[13, 6]
[14, -2.0000000000000004]
[15, 7]
[17, 8]
[18, 2.000000000000001]
[19, 9]
[20, true]
[21, 10]
[22, -2.000000000000001]
[23, 11]
...
like image 51
akuhn Avatar answered Sep 19 '22 18:09

akuhn


tl;dr: it doesn't matter, you can't tell, and since you can't tell, the Ruby Language Specification doesn't say anything about it, which allows the different implementors to make different choices, and they do in fact make different choices.


It doesn't matter.

The only way you could tell the difference is by modifying the object, but since all immediate objects are immutable, there is no way for you to tell one way or the other.

As it turns out, different Ruby implementations treat them differently, and there is nothing wrong with that. For example, YARV stores Integers as tagged pointers (called fixnums) or object references (called bignums) depending on size, but again, the very same number may be stored as either, because on 64 bit systems, it uses 63 bits for fixnums, and on 32 bit systems, it only uses 31 bit. JRuby OTOH doesn't use tagged pointers (it doesn't use pointers at all, since Java simply doesn't have them), and uses full 64 bits for fixnums regardless of machine word size, instead of YARV which uses 31 or 63 bits.

Likewise, YARV on 64 bit systems uses a 62 bit tagged pointer format for Floats which fit in 62 bits (which they call flonums), but on 32 bit systems and for larger Floats, it uses a different encoding.

like image 33
Jörg W Mittag Avatar answered Sep 20 '22 18:09

Jörg W Mittag