Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby and pointers compared to other languages?

Tags:

c++

arrays

ruby

I am fairly new to ruby but coming from a C/C++ background and I believe I have basic knowledge within how computers and programming work.

In C++ if you would like to create a pointer which will point to a value you will specify it as a pointer: http://www.cplusplus.com/doc/tutorial/pointers/.

Example:

int x = 3;
int *p = &x;
*p = 4;
std::cout << x;

Which makes sense to me because now p is pointing to the address of x and therefore when we change the value of p, x will change as well. x will in this case output 4.

But when I run:

a = []
b = a
a[0] = 3
p b
# => [3]

c = "this is a string"
d = c
c.upcase!
c = "the string has been edited"
p d
# => THIS IS A STRING

In ruby, b outputs [3] where it for me should output nothing. And doutputs "THIS IS A STRING" and not "the string has been edited". Ruby and pointers is another question where they go through how you can use pointers in ruby.

My question is: Why are arrays seen as pointers in ruby and how does it work? Is bpointing to a's address as in C++? What makes a variable change what it is pointing to? For example: if a = b when will a != b? Why are integers not pointing to the address of another variable?

like image 490
darclander Avatar asked Sep 16 '25 15:09

darclander


2 Answers

Why are arrays seen as pointers in ruby and how does it work?

They aren't. There is no such thing as a "pointer" in Ruby.

Is bpointing to a's address as in C++?

There is no such thing as an "address" in Ruby. The Language Specification says that when you dereference a variable, it evaluates to the last object that was assigned to that variable. How the language implementation does this is completely and utterly irrelevant, and you shouldn't, and in fact can't know about it. It could use a pointer to memory location. It could use a dictionary. It could print the value out on paper and fax it to a sweat shop in East Asia where child slaves interpret it. (That would be highly immoral, unethical, and illegal, but it would be a perfectly valid implementation.)

What makes a variable change what it is pointing to?

Assignment, and only assignment can change the binding of a variable.

For example: if a = b when will a != b?

When the != method is overridden to say so. Which would be a terrible idea, but nonetheless:

class Foo
  def !=(*)
    false
  end
end

a = Foo.new
b = a

a != b #=> false

Why are integers not pointing to the address of another variable?

Again, there is no such thing as a "pointer" or an "address" in Ruby, therefore, an integer can never "point" to an "address", simply because "pointing" and "address" are not concepts that exist in Ruby.

Let's step through your code line-by-line:

a = []

You create a new Array object and bind the local variable a to that new array object.

b = a

Now, you bind the local variable b to the result of evaluating the expression a. What is the result of evaluating an expression that consists solely of the name of a variable? The variable gets dereferenced, i.e. it evaluates to the object that was bound to the variable earlier.

In this case, that object is the Array that you created on line 1.

a[0] = 3

This line is simply syntactic sugar for the following line:

a.[]=(0, 3)

Here, you are sending the message []= passing the two arguments 0 and 3 to the result of dereferencing the local variable a. Again, dereferencing the local variable a evaluates to whatever the variable was bound to last, which is the Array from line 1. The method Array#[]= is defined as mutating its receiver such that it places its second argument at the index specified by the first argument.

The method doesn't have to mutate its receiver. It could also return an updated copy of the Array. (Although in this particular case that would be useless since the a[0] = 3 syntax sugar behaves like assignment and thus will always evaluate to the right-hand side. In other words, the return value of Array#[]= would be ignored unless you use the explicit method calling syntax a.[]=(0, 3).)

p b

Here, we again dereference the local variable b, which still points to the exact same object (there was never any re-assignment), which is still the Array from line 1.

There was only ever one single Array in the entire code. You never created a second one, and you never called a method that would return a new Array.

One method that does return a new Array as opposed to mutating the receiver is the method Array#+. Here is an alternate example using Array#+:

a = []
b = a
a += [3]

a += [3] is equivalent to a = a + [3] which is equivalent to a = a.+([3]). So, we dereference a, which gives the Array from line 1, then we send it the message + with the single argument [3]. Array#+ is defined to return a new Array which is the concatenation of the receiver and the argument. Lastly, we assign this new Array to the local variable a.

p b

The local variable b is still bound to the same object, since we never re-assigned it. So, it is still bound to the Array from line 1. We never changed this Array, ergo, this prints [].

On to your second example (I'll go a bit faster this time):

c = "this is a string"

Create a new String, assign to c.

d = c

Bind d to whatever c is bound to, i.e. the String from line 1.

c.upcase!

Send the message upcast! to the String obtained by dereferencing c. String#upcase! is defined as mutating the receiver. So, the String that is being referenced by both c and d has now changed itself.

c = "the string has been edited"

Create a brand-new String that has nothing to do with the other one and assign it to c.

p d

d was never re-assigned, ergo, it is still bound to the String from line 1.

like image 109
Jörg W Mittag Avatar answered Sep 18 '25 08:09

Jörg W Mittag


In general - ruby variables hold references, not pointers to the values. When you assign:

a = [3]
b = a

b references the same array as a. It's the same with strings.

Worth reading: https://robertheaton.com/2014/07/22/is-ruby-pass-by-reference-or-pass-by-value/

EDIT:

a = [1]
b = a
b
# => [1]
a = [2]
a
# => [2]
b
# => [1]

like image 35
mrzasa Avatar answered Sep 18 '25 09:09

mrzasa