Some bang version of Array
methods are like compact!
, reject!
, flatten!
, uniq!
return nil
if no changes were made:
[1,[2]].flatten!
# => [1, 2]
[1,2].flatten!
# => nil
[1,[2]].flatten
# => [1, 2]
[1,2].flatten
# => [1, 2]
[1,2,nil].compact!
# => [1, 2]
[1,2].compact!
# => nil
[1,2,nil].compact
# => [1, 2]
[1,2].compact
# => [1, 2]
If they did it this way, there has to be a reason. Any ideas what it might be?
The bang (!
) methods do modify the current object in place, but they do return nil
if there are no affected elements per the documentation. This is useful if, for whatever reason, you need to do something if you did modify the array in question.
if array.flatten!
puts "Oh yeah... flattened that array!"
end
I was always under impression that
bang version of Array
methods are
only different in the way that they
modify object in place.
Perhaps the problem here is that this impression is not really a correct one: according to David A. Black, !
does not mean that the method changes its receiver; !
means that this method is the "dangerous" version of an otherwise equivalent method, which has the same name minus the !
.
Now danger takes many forms (emphasis mine):
Sometimes you get more than one kind
of "danger" even within one bang
method. Take String#gsub!
. This
method changes its receiver:
str = "David"
str.gsub!(/$/, " Black")
str # David Black
It also differs from gsub
(non-bang)
in that if the string does not change,
gsub
returns a copy of the unchanged
string but gsub!
returns nil:
str.gsub(/xyz/, "blah") # David Black
str.gsub!(/xyz/, "blah") # nil
str # David Black
The ! in gsub!
gives you a heads-up:
it warns you of danger, and that means
that before you use the method, you
should find out exactly how it
behaves. (A simple "ri String#gsub!
"
should do it.)
This "heads-up" semantics also applies to the bang methods of Array
.