1

Background:

I discovered (albeit accidentally) that using a ! method (e.g. #sub!, #chomp!, etc.) within a user-defined function can actually change the way an object gets passed (i.e. by value or by reference) to that function (or at least it appears that way).

I know that ! methods are used to alter an object in-place, but honestly I've always thought that using them (or not) was just a matter of preference. I, for one, have always preferred to use the ! methods for the slightly briefer syntax (e.g. str.chomp! instead of str = str.chomp). However, the difference can actually be detrimental in some cases. Allow me to explain with the following example.

Example:

The example below defines two functions, change1 and change2, that alter the value of str to say, "CHANGED!" using the #sub and #sub! methods, respectively. They seemingly do the same thing, but notice that the value of orig gets altered by change2 but not by change1.

I guess I figured that orig gets passed by value in both cases, and therefore the two functions act exactly the same way. But I was wrong.

# Pass by value
def change1(str)
  str = str.sub(/^.*$/, 'CHANGED!') # Changes 'str' but not 'orig' (as expected)
  return str
end
# Pass by reference...?
def change2(str)
  str.sub!(/^.*$/, 'CHANGED!') # Changes 'str' AND 'orig' (unexpected!)
  return str
end

orig = "original"
p change1(orig)  #=> "CHANGED!"
p orig           #=> "original"
p change2(orig)  #=> "CHANGED!"
p orig           #=> "CHANGED!"

Questions:

If objects are passed by value in Ruby, how does the use of a ! method such as str.sub!(...) alter an object that's not even in the same scope (seemingly passing it by reference)?

Likewise, why does the use of a non-! method such as str = str.sub(...) act differently?

Most importantly, how could I have ever known that these two types of methods (seemingly) affect the way an object gets passed to my function? For example, the documentation for #sub! only states that it "Performs the same substitution as #sub in-place."

seane
  • 589
  • 1
  • 4
  • 15

0 Answers0