30

In Ruby, is it possible to pass by reference a parameter with value-type semantics (e.g. a Fixnum)? I'm looking for something similar to C#'s 'ref' keyword.

Example:

def func(x) 
    x += 1
end

a = 5
func(a)  #this should be something like func(ref a)
puts a   #should read '6'

Btw. I know I could just use:

a = func(a)
Ry-
  • 218,210
  • 55
  • 464
  • 476
Cristian Diaconescu
  • 34,633
  • 32
  • 143
  • 233

6 Answers6

32

You can accomplish this by explicitly passing in the current binding:

def func(x, bdg)
  eval "#{x} += 1", bdg
end

a = 5
func(:a, binding)
puts a # => 6
jmah
  • 2,216
  • 14
  • 16
20

Ruby doesn't support "pass by reference" at all. Everything is an object and the references to those objects are always passed by value. Actually, in your example you are passing a copy of the reference to the Fixnum Object by value.

The problem with the your code is, that x += 1 doesn't modify the passed Fixnum Object but instead creates a completely new and independent object.

I think, Java programmers would call Fixnum objects immutable.

Christoph Schiessl
  • 6,818
  • 4
  • 33
  • 45
  • 4
    I don't get this thing that Ruby doesn't pass by ref, when variables and parameters can only contain references to objects. Saying that references are passed by value is the same as saying that objects are passed by reference. – Damien Pollet May 03 '09 at 13:17
  • 2
    @Damien: no it isn't. Pass by reference (e.g. in C++, C#, PHP, Perl) allows you to *assign* to the variable as if you assigned to it in the outer scope. What you're trying to do here would be possible with true pass by reference; but not with passing by value of references. – newacct Apr 23 '12 at 17:34
  • 8
    You're right, I'm reading Christoph's answer and my comment again and I don't agree with my 2009-self. Hmm… – Damien Pollet Apr 24 '12 at 01:50
12

In Ruby you can't pass parameters by reference. For your example, you would have to return the new value and assign it to the variable a or create a new class that contains the value and pass an instance of this class around. Example:

class Container
attr_accessor :value
 def initialize value
   @value = value
 end
end

def func(x)
  x.value += 1
end

a = Container.new(5)
func(a)
puts a.value
muesan
  • 373
  • 3
  • 8
8

You can try following trick:

def func(x) 
    x[0] += 1
end

a = [5]
func(a)  #this should be something like func(ref a)
puts a[0]   #should read '6'
ShockwaveNN
  • 2,227
  • 2
  • 29
  • 56
Ivan Gusev
  • 252
  • 5
  • 10
  • Technically this is also replacing the variable (and reference in the list) on write. – itarato Oct 15 '17 at 23:04
  • @itarato Both a and x point to the same list throughout their lifetimes, so nothing is replacing any variable, but the list's first entry is changed to point to a different number. I like this trick because using a list feels like less mental overhead than a wrapper class .get/.set methods, but it does mean you need to remember to dereference it with [0]. – Tim Baverstock Sep 09 '20 at 12:56
2

http://ruby-doc.org/core-2.1.5/Fixnum.html

Fixnum objects have immediate value. This means that when they are assigned or passed as parameters, the actual object is passed, rather than a reference to that object.

Also Ruby is pass by value.

Community
  • 1
  • 1
Sam Liao
  • 43,637
  • 15
  • 53
  • 61
-1

However, it seems that composite objects, like hashes, are passed by reference:

fp = {}
def changeit(par)
  par[:abc] = 'cde'
end

changeit(fp)

p fp

gives

{:abc=>"cde"}
Ivan Cenov
  • 189
  • 2
  • 10
  • The hash is passed by value, but the value is a reference to a mutable object. So fp always points to the same hash object instance, but changeit modifies its contents. You can confirm this by printing out fp.object_id and par.object_id. You can't, merely by assigning a different hash to par, influence the value of fp - which is what 'pass by reference' would permit. – Tim Baverstock Sep 09 '20 at 13:04