Search code examples
rubysplat

Pass arguments by reference to a block with the splat operator


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.


Solution

    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.