Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Object assignment and pointers

I am a little confused about object assignment and pointers in Ruby, and coded up this snippet to test my assumptions.

class Foo
    attr_accessor :one, :two
    def initialize(one, two)
        @one = one
        @two = two
    end

end

bar = Foo.new(1, 2)
beans = bar

puts bar
puts beans

beans.one = 2
puts bar
puts beans
puts beans.one
puts bar.one

I had assumed that when I assigned bar to beans, it would create a copy of the object, and modifying one would not affect the other. Alas, the output shows otherwise.

^_^[jergason:~]$ ruby test.rb 
#<Foo:0x100155c60>
#<Foo:0x100155c60>
#<Foo:0x100155c60>
#<Foo:0x100155c60>
2
2

I believe that the numbers have something to do with the address of the object, and they are the same for both beans and bar, and when I modify beans, bar gets changed as well, which is not what I had expected. It appears that I am only creating a pointer to the object, not a copy of it. What do I need to do to copy the object on assignment, instead of creating a pointer?

Tests with the Array class shows some strange behavior as well.

foo = [0, 1, 2, 3, 4, 5]
baz = foo
puts "foo is #{foo}"
puts "baz is #{baz}"
foo.pop
puts "foo is #{foo}"
puts "baz is #{baz}"

foo += ["a hill of beans is a wonderful thing"]
puts "foo is #{foo}"
puts "baz is #{baz}"

This produces the following wonky output:

foo is 012345
baz is 012345
foo is 01234
baz is 01234
foo is 01234a hill of beans is a wonderful thing
baz is 01234

This blows my mind. Calling pop on foo affects baz as well, so it isn't a copy, but concatenating something onto foo only affects foo, and not baz. So when am I dealing with the original object, and when am I dealing with a copy? In my own classes, how can I make sure that assignment copies, and doesn't make pointers? Help this confused guy out.

like image 778
jergason Avatar asked Apr 14 '10 05:04

jergason


People also ask

What are objects and pointers?

A pointer is a type of variable that carries location information. In this case, the example variable will store the address of an Order object that we want to interact with. We initialize the pointer variable by using the C++ new operator to construct a new object of type Order.

What is pointers in assignment?

In ordinary assignment involving pointers, the pointer is an alias for its target. In pointer assignment, the pointer is associated with a target. If the target is undefined or disassociated, the pointer acquires the same status as the target.

How do you assign a pointer to an object?

Just like Other Pointers, the object pointers are declared by placing in front of a object pointer's name. The Syntax is: class_name * Object_pointer_name; In above Syntax, class_Name is the name of an already defined class and object_pointer_name is the pointer to an object of this class type.


3 Answers

There are a lot of questions in this question. The main thing to know is assignment never makes a copy in ruby, but methods often return new objects instead of modifying existing objects. For immutable objects like Fixnums, you can ignore this, but for objects like arrays or Foo instances, to make a copy you must do bar.dup.

As for the array example, foo += is not concatenating onto the array stored in foo, to do that you would do foo.concat(['a']). Instead, it is making a new array and assigning foo to that. The documentation for the Array class mentions which methods mutate the array in place and which return a new array.

like image 85
mckeed Avatar answered Nov 15 '22 05:11

mckeed


+ and - in Array each return new arrays filled with the respective content, so foo += [...] not affecting baz is normal. Try the << operator on foo and the result will be baz seeing the same change.

I'm not sure how Ruby handles the other thing internally but you might try using one.clone and two.clone in Foo#initialize.

like image 35
hermannloose Avatar answered Nov 15 '22 06:11

hermannloose


You never deal with a copy. It's the same object in memory, but you just declare 2 references to it: in your first example: bar and beans point towards the same object in memory; and in your second example: foo and baz point to the same array in memory initially.

Check out the 2 picture/drawings, in the Java tutorial page that explains the mechanism (it's the same as in Ruby) and those 2 pictures only, worth more than any explanation in words: http://docs.oracle.com/javase/tutorial/java/javaOO/objectcreation.html

like image 44
Luisa Avatar answered Nov 15 '22 07:11

Luisa