Featured image

If you’ve been working with Ruby for a while, you probably know that arguments are passed into methods by reference to the object (more specifically, pass-by-sharing). Because of this, you cannot simply reassign a new value to an argument inside a method and expect it to change outside. Technically, you can assign it, but once the method finishes execution, the outer variable will still hold its original value. For example:

def change(a)
  a = 'other string'
  nil
end

s = 'some string'
change(s)
puts s # some string

One way to solve this is to wrap the incoming parameter in a collection or create a custom wrapper class to encapsulate your value. To put it clearly, it looks something like this:

def change(a)
  a[0] = 'other string'
  nil
end

s = 'some string'
a = Array(s)
change(a)
s = a.first
puts s # other string


def change(v)
  v.set('other string')
  nil
end

Value = Struct.new(:v) do
  def set(v) = self.v = v
  def get = v
end

s = 'some string'
v = Value.new(s)
change(v)
s = v.get
puts s # other string

However, just a few days ago, while browsing the I/O streams documentation (IO#read), I accidentally stumbled upon another way to mutate an incoming parameter’s value directly:

require 'stringio'
def change(str)
  StringIO.new('other string').read(nil, str)
  nil
end

s = 'some string'
change(s)
puts s # other string

Unfortunately, this trick only works for strings. I wonder, are there any other ways to directly mutate an argument’s value inside a method?