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?