David Wright David Wright - 3 months ago 16
Ruby Question

How to pass by reference in Ruby?

Currently I'm developing a

Watcher
class in Ruby, which, among other things, is able to find the period duration of a toggling signal. This is in general rather simple, but a problem I'm facing is that apparently Ruby passes all parameters by value.
While researching online I found many different discussions about what "pass by value" and "pass by reference" actually is, but no actual "how to". Coming from a C/C++ background, for me this is an essential part of a programming/scripting language.

The relevant parts of the class that I wrote are shown below. The method Watcher::watchToggling() is the one I'm having trouble with. As a parameter,
aVariable
should be the reference to the value I'm measuring the period duration for.

class Watcher
@done
@timePeriod

def reset()
@done = false;
@timePeriod = nil;
end

def watchToggling(aVariable, aTimeout=nil)
oldState = aVariable;
oldTime = 0;
newTime = 0;

timeout(aTimeout)do
while(!@done)
newState = aVariable;
if(newState != oldState)
if(newState == 1)
# rising edge
if(oldTime != 0)
# there was already a rising edge before,
# so we can calculate the period
newTime = Time.now();
@timePeriod = newTime - oldTime;
@done = true;
else
# if there was no previous rising edge,
# set the time of this one
oldTime = Time.now();
end
end
oldState = newState;
end
end
end

puts("Watcher: done.")
end

def isDone()
return @done;
end

def getTimePeriod()
return @timePeriod;
end
end


Is there any way at all in ruby to pass by reference? And if not, are there alternatives that would solve this problem?

Answer

Ruby is strictly pass-by-value, which means references in the caller's scope are immutable. Obviously they are mutable within the scope, since you can assign to them after all, but they don't mutate the caller's scope.

a = 'foo'

def bar(b)
  b = 'bar'
end

bar(a)

a
# => 'foo'

Note, however, that the object can be mutated even if the reference cannot:

a = 'foo'

def bar(b)
  b << 'bar' # the only change: mutate the object instead of the reference
  b = 'baz'
end

bar(a)

a
# => 'foobar'

If the object you get passed is immutable, too, then there is nothing you can do. The only possibilities for mutation in Ruby are mutating the reference (by assignment) or asking an object to mutate itself (by calling methods on it).

You can return an updated value from your method and have the caller assign that to the reference:

a = :foo

def bar(b)
  :"#{a}bar"
end

c = bar(a)

c
# => :foobar

Or you can wrap the immutable object in a mutable one and mutate that mutable wrapper:

a = [:foo]

def bar(b)
  b[0] = :bar
end

bar(a)

a
# => [:bar]

[This is really the same thing as the second example above.]

But if you can't change anything, then you are stuck.