upcase!
(to get the
all-uppercase version of a string) and uniq!
(to get the unique
elements of an array). Long story short, the bang means that you should
use it with caution.
Usually this is because it modifies the object passed and returns it, as opposed to returning a modified copy of it. (In Ruby on Rails and many similar frameworks, this may also be because it will throw an exception if anything goes wrong. However, we will focus on core Ruby methods.) I'll show you another reason in a moment, but for now, let's just examine the normal usually-expected behavior. For instance:
will output FOO and then foo. Whilestr = 'foo' p str.upcase p str
upcase
returned the
uppercased version of str
, it did not modify
str
. On the other claw, if we add a bang, doing:
we get FOO and then FOO again! In other words,str = 'foo' p str.upcase! p str
upcase!
returned the uppercased version, just as the non-bang version did, but it
also uppercased str
itself!
Similarly, if we use
uniq
:
we get [1, 3, 7] and then [1, 3, 3, 7], showing again that the non-bang version returned the unique values withinarr = [1, 3, 3, 7] p arr.uniq p arr
arr
, but did not modify
arr
, whereas if we add a bang and do:
we get [1, 3, 7] and then [1, 3, 7] again, showing thatarr = [1, 3, 3, 7] p arr.uniq! p arr
arr
itself was modified this time.
So far so good.
But wait! There's more! There's another big bad gotcha waiting to getcha!
Do not depend on the bang versions returning the same value as the non-bang versions! (Even though that value seems to be the whole point of both functions!)
In the specific cases above, yes they do. But let's look at what happens if the variable is already how we want -- in other words, if the string is already all uppercase, or the array already has only unique values. If we do:
then, as expected, since it already fit our needs,str = 'FOO' p str.upcase! p str
str
is
unchanged. But look at str.upcase!
-- it's nil
!
Let's see what happens in the numeric case. If we do:
then, just as above,arr = [1, 3, 7] p arr.uniq! p arr
arr
is unchanged... but
arr.uniq!
is nil
! How come?
Long story short, standard Ruby bang methods often return
nil
if no change was needed.
Worse yet, even that is not completely consistent. When using any bang-method that you are not already very familiar with, be sure to RTFM.