Brandon Frohbieter
Brandon Frohbieter

Reputation: 18139

ruby variable as same object (pointers?)

>> a = 5
=> 5
>> b = a
=> 5
>> b = 4
=> 4
>> a
=> 5

how can I set 'b' to actually be 'a' so that in the example, the variable a will become four as well. thanks.

Upvotes: 5

Views: 5933

Answers (7)

Michael Johnston
Michael Johnston

Reputation: 5320

One option in cases where you feel you would like to have direct pointer operations is to use the replace method of Hashes, Arrays & Strings.

this is useful for when you would like to have a method return a variable that a proc the method sets up will change at a later date, and don't want the annoyance of using a wrapper object.

example:

def hash_that_will_change_later
  params = {}
  some_resource.on_change do
    params.replace {i: 'got changed'}
  end
  params
end
a = hash_that_will_change_later
=> {}
some_resource.trigger_change!
a
{i: 'got changed'}

It's probably better generally to use explicit object wrappers for such cases, but this pattern is useful for building specs/tests of asynchronous stuff.

Upvotes: 1

Douglas G. Allen
Douglas G. Allen

Reputation: 2261

Just for the sake of reference.

>> a = 5
=> 5
>> a.object_id
=> 11
>> b = a
=> 5
>> b.object_id
=> 11
>> b = 4
=> 4
>> b.object_id
=> 9
>> a.object_id
=> 11
# We did change the Fixnum b Object.
>> Fixnum.superclass
=> Integer
>> Integer.superclass
=> Numeric
>> Numeric.superclass
=> Object
>> Object.superclass
=> BasicObject
>> BasicObject.superclass
=> nil

I hope this gives us all a little better understanding about objects in Ruby.

Upvotes: 1

Sony Santos
Sony Santos

Reputation: 5545

You can use arrays:

a = [5]
b = a
b[0] = 4
puts a[0]  #=>  4

This idea is based on this answer.

Upvotes: 1

Sony Santos
Sony Santos

Reputation: 5545

class Ref
  def initialize val
    @val = val
  end

  attr_accessor :val

  def to_s
    @val.to_s
  end
end

a = Ref.new(4)
b = a

puts a   #=> 4
puts b   #=> 4

a.val = 5

puts a   #=> 5
puts b   #=> 5

When you do b = a, b points to the same object as a (they have the same object_id).

When you do a = some_other_thing, a will point to another object, while b remains unchanged.

For Fixnum, nil, true and false, you cannot change the value without changing the object_id. However, you can change other objects (strings, arrays, hashes, etc.) without changing object_id, since you don't use the assignment (=).

Example with strings:

a = 'abcd'
b = a

puts a  #=> abcd
puts b  #=> abcd

a.upcase!          # changing a

puts a  #=> ABCD
puts b  #=> ABCD

a = a.downcase     # assigning a

puts a  #=> abcd
puts b  #=> ABCD

Example with arrays:

a = [1]
b = a

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

a << 2            # changing a

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

a += [3]          # assigning a

p a  #=> [1, 2, 3]
p b  #=> [1, 2]

Upvotes: 6

I'm no Ruby expert. But for a technically crazy kluge...that would only work if you felt like going through eval every time you worked with a variable:

>> a = 5
=> 5
>> b = :a
=> :a
>> eval "#{b} = 4"
=> 4
>> eval "#{a}"
=> 4
>> eval "#{b}"
=> 4

Note that a direct usage of b will still give you :a and you can't use it in expressions that aren't in eval:

>> b
=> :a
>> b + 1
NoMethodError: undefined method `+' for :a:Symbol

...and there are certainly a ton of caveats. Such as that you'd have to capture the binding and pass it around in more complex scenarios...

'pass parameter by reference' in Ruby?

@Paul.s has an answer for if you can change the point of declaration to be a wrapper object, but if you can only control the point of reference then here's a BasicReference class I tried:

class BasicReference
    def initialize(r,b)
        @r = r
        @b = b
        @val = eval "#{@r}", @b
    end

    def val=(rhs)
        @val = eval "#{@r} = #{rhs}", @b
    end

    def val
        @val
    end
end

a = 5

puts "Before basic reference"
puts "   the value of a is #{a}"

b = BasicReference.new(:a, binding)

b.val = 4

puts "After b.val = 4"
puts "   the value of a is #{a}"
puts "   the value of b.val is #{b.val}"

This outputs:

Before basic reference
   the value of a is 5
After b.val = 4
   the value of a is 4
   the value of b.val is 4

Upvotes: 0

Paul.s
Paul.s

Reputation: 38728

As has been noted the syntax you are using can not be done. Just throwing this out there though you could make a wrapper class it depends what you actually want to do

ruby-1.8.7-p334 :007 > class Wrapper
ruby-1.8.7-p334 :008?>   attr_accessor :number
ruby-1.8.7-p334 :009?>   def initialize(number)
ruby-1.8.7-p334 :010?>     @number = number
ruby-1.8.7-p334 :011?>   end
ruby-1.8.7-p334 :012?> end
 => nil 
ruby-1.8.7-p334 :013 > a = Wrapper.new(4)
 => #<Wrapper:0x100336db8 @number=4> 
ruby-1.8.7-p334 :014 > b = a
 => #<Wrapper:0x100336db8 @number=4> 
ruby-1.8.7-p334 :015 > a.number = 6
 => 6 
ruby-1.8.7-p334 :016 > a
 => #<Wrapper:0x100336db8 @number=6> 
ruby-1.8.7-p334 :017 > b
 => #<Wrapper:0x100336db8 @number=6> 

Upvotes: 1

maerics
maerics

Reputation: 156414

You can't. Variables hold references to values, not references to other variables.

Here's what your example code is doing:

a = 5 # Assign the value 5 to the variable named "a".
b = a # Assign the value in the variable "a" (5) to the variable "b".
b = 4 # Assign the value 4 to the variable named "b".
a # Retrieve the value stored in the variable named "a" (5).

See this article for a more in-depth discussion of the topic: pass by reference or pass by value.

Upvotes: 2

Related Questions