Question

Coming from a c++ background I'm curious about object assignment in Ruby. What considerations (if any) should be made for the following object assignments:

class MyClass

  attr_accessor :a, :b

  def initialize(a, b)
    @a = a
    @b = b
  end

  def some_method
    puts "#{self.a} #{self.b}"
  end
end

m = MyClass.new("first", "last")
n = MyClass.new("pizza", "hello")

q = n
q.some_method
Was it helpful?

Solution

If you're familiar with C++, then you might want to consider every variable in Ruby, instance or otherwise, as a reference to another object. Since everything in Ruby is an object, even nil, which is of type NilClass, this holds true under every circumstance.

To determine which object you're referencing, you can use the object_id method to differentiate. That's similar to converting to a pointer using & in C++.

Consider this:

a = "foo"
b = a

a.object_id == b.object_id
# => true

Since a is a reference to that string, and b is a copy of a, then they are actually different references to the same object.

This is important because operations that modify an object affect all references to that equally:

a << "bar"
# => "foobar"
b
# => "foobar"

However, operations that create a new object will not modify all copies:

a += "baz"
# => "foobarbaz"
b
# => "foobar"

Many methods in Ruby are identified by a ! to distinguish in-place versus new-copy versions, but this is not always the case, so you have to be familiar with each method in order to be sure.

Generally an assignment will replace an old reference with a new one, so as a rule of thumb, = will replace old references. This applies to +=, -=, ||=, &&= and so on.

Edit: Updated based on Phrogz's comment about using ObjectSpace._id2ref(object_id) to convert an object identifier into an object.

OTHER TIPS

Since everything is an object in ruby, assignment is always by reference.

So, taking your class as an input, the following will be the output for several operations:

str = "foo"
foo = MyClass.new(str, "bar")
foo.some_method # foo bar
bar = foo
bar == foo # true
bar.some_method # foo bar
str << "bar" # strings are mutable on ruby, so str is now "foobar"
foo.some_method # foobar bar
bar.some_method # foobar bar

I would rewrite this as:

class MyClass

  attr_accessor :a, :b

  def initialize(a, b)
    self.a = a
    self.b = b
  end

  def some_method
    puts "#{a} #{b}"
  end
end

This way you're actually using the getter/setter methods defined by attr_accessor within your class

When you assign q = n, q just references the same memory location that was set for n. The object isn't copied.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top