Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass arguments by reference to a block with the splat operator

Tags:

ruby

splat

It seems that the arguments are copied when using the splat operator to pass arguments to a block by reference.

I have this:

def method
  a = [1,2,3]
  yield(*a)
  p a
end

method {|x,y,z| z = 0}
#=> this puts and returns [1, 2, 3] (didn't modified the third argument)

How can I pass these arguments by reference? It seems to work if I pass the array directly, but the splat operator would be much more practical, intuitive and maintainable here.

like image 578
alf Avatar asked Jan 15 '23 23:01

alf


2 Answers

  1. In Ruby when you write x = value you are creating a new local variable x whether it existed previously or not (if it existed the name is simply rebound and the original value remains untouched). So you won't be able to change a variable in-place this way.

  2. Integers are immutable. So if you send an integer there is no way you can change its value. Note that you can change mutable objects (strings, hashes, arrays, ...):

    def method
      a = [1, 2, "hello"]
      yield(*a)
      p a
    end
    
    method { |x,y,z| z[1] = 'u' }
    # [1, 2, "hullo"]
    

Note: I've tried to answer your question, now my opinion: updating arguments in methods or blocks leads to buggy code (you have no referential transparency anymore). Return the new value and let the caller update the variable itself if so inclined.

like image 109
tokland Avatar answered Jan 31 '23 08:01

tokland


The problem here is the = sign. It makes the local variable z be assigned to another object.

Take this example with strings:

def method
  a = ['a', 'b', 'c']
  yield(*a)
  p a
end

method { |x,y,z| z.upcase! }   # => ["a", "b", "C"]

This clearly shows that z is the same as the third object of the array.

Another point here is your example is numeric. Fixnums have fixed ids; so, you can't change the number while maintaining the same object id. To change Fixnums, you must use = to assign a new number to the variable, instead of self-changing methods like inc! (such methods can't exist on Fixnums).

like image 33
Sony Santos Avatar answered Jan 31 '23 09:01

Sony Santos