1

I asked this question not to long ago - "Does 'upcase!' not mutate a variable in Ruby? ". As a follow up, I changed the code a bit and I'm still not sure what's going on. Why doesn't + modify the object that str is pointing to? I'm not re-assigning str again right?

def change_string(str)
  str + "?"
  str.upcase!
end

phrase = "what time is it"
change_string(phrase)
puts phrase

The phrase is upcased but the question mark is not added. If I remove the last line that upcases the object then the "?" is appended. Why is this? Is it because + is non destructive and upcase! is? Or is there something else happening here that I'm missing.

  • 1
    `str + "?"` doesn't modify `str`. All that does is say "create a new string by combining `str` and ? but don't assign it to a variable." The reason it works if you remove the `str.upcase!` line is that in Ruby, the last line of a method is its return value. Therefore, when `str + "?"` is the last line of your method, that's your return value. – anothermh Jul 03 '20 at 15:57
  • thank you, that helps. – Subscription Services Jul 03 '20 at 16:05
  • Instead, you can use `str << "?"`, which will modify the original string (as opposed to `+` which will by default create a copy). – johansenja Jul 03 '20 at 16:21
  • @anothermh last line is returned but she hasn't stored anywhere so it still wouldn't print `?` so no change. – Rajagopalan Jul 03 '20 at 16:24
  • 1
    A variable cannot be mutated because it is not an object. A variable references an object, identified by a unique object id. Through assignment the variable can be made to reference a different object. Alternatively, the object referenced by a variable can be modified, or mutated. Suppose you've named your phone "Sue" and it stops working, so you take it in for repair. If the phone is replaced, you may decide to assign the new phone to the same name "Sue" ("Sue" now references a different object). If the phone is repaired, or mutated, "Sue" continues to reference the same old phone. – Cary Swoveland Jul 03 '20 at 17:58

1 Answers1

2

It's important to remember what variables are in Ruby, and that's effectively references to objects, or in more technical terms, a fancy pointer with integral reference counting.

What that means is something like str is, say, just a reference to some String object. A method like upcase! alters that object. A method like upcase does not, it returns a new object. The differences are explained in the documentation, which you must check in order to be sure you've got it right. The ! alone is not enough to go on, as methods like Array#pop alter the array and don't have that decoration.

When you write a line like this:

str + '?'

That does create a new object, but as you never actually capture that to a variable or use it as an argument it's immediately discarded.

What you meant was:

str = str + '?'

Or more concisely:

str += '?'

Where both of those create a new object, then make str a reference to this new object.

tadman
  • 208,517
  • 23
  • 234
  • 262