0

puts 'A'.upcase! returns nil. This is kind of confusing, especially since 'Ab' returns 'AB', not 'B'. Is this a bug or a language quirk?

edit so I see the docs, but it still seems counterintuitive. When would this make sense in a real usage scenario? I could see it causing more problems than helping

edit Tihom's answer seems like the best one; the post and comments he refers to brought me to understand that Matz and the Ruby dev team were most likely motivated by consistency in the integrity of the data types (that a string should be immutable) rather than the sensibility of the chained method in spoken English.

It seems that the upcase! for single characters is a bit of an odd duck for which there's no practical use and is a bit of a pathological case that really is counterintuitive for english speakers. Tihom notes that other instances of Ruby's prioritization of string immutability are more reasonable:

A more intuitive example is gsub!, it returns nil if no substitution was done:

"abc".gsub!('d','') #=> nil

inman320
  • 501
  • 3
  • 13
  • 1
    *'Ab' returns 'AB', not 'B'* - why do you expect `'B'` ? – Arup Rakshit Oct 26 '13 at 19:51
  • because the single capital character upcased returns nil, I would expect multiple to return nil, which it does, sometimes: "ABCDEFG" returns nil, but "ABCDEFGhi" gives "ABCDEFGHI" – inman320 Oct 26 '13 at 19:56
  • Look the line carefully - **Upcases the contents of str,when all the characters of the string is not upcased.Then return the str** Now when all the characters of the string is already upcased,then no change is made,so `nil` is returned. This is how `String#upcase!` works. – Arup Rakshit Oct 26 '13 at 19:58
  • The answers so far merely state how `upcase!` works. I think you know that. Your question is really the one beginning, 'edit'. I'd be interested in the answer as well. – Cary Swoveland Oct 26 '13 at 20:09
  • @CarySwoveland I tried to answer him in my comment..Hope OP got the point.. :) – Arup Rakshit Oct 26 '13 at 20:11
  • @Arup, I trust you're not suggesting `str.upcase!` returns nil so that `str.upcase!` (or let's say, `str.dup.upcase!`) could be used to check whether a string is all uppercase. In my mind, the question is: why did Matz decide to decide to define `upcase!`, (but not `upcase`), in this way, considering that `'AB'.upcase! => 'AB'` seems (to me anyway) more intuitive and more useful. – Cary Swoveland Oct 26 '13 at 21:02
  • Here are some more data points, should it be of interest: `''.slice!(0,0) #=>"", 'ab'.slice(3,4) #=>nil, 'Ab'.capitalize! #=>nil, ''.chop! #=>nil, ''.reverse! #=>"", [].reverse! #=>[], [].sort! #=>[], [1,2,3].flatten # =>[1,2,3], [1,2,3].flatten! # =>nil, [].slice(4,5) # =>nil, [].slice!(4,5) # =>nil, [1,2,3].slice(4,5) # =>nil, [1,2,3].compact! # =>nil, {a:1}.reject {||} # => {:a=>1, {a:1}.reject! {||} # =>nil, str.reverse! returns str when str is a palindrome.` inman, if you want to edit your question to include any of these, so they can be read more easily, feel free to do so. – Cary Swoveland Oct 26 '13 at 22:22
  • @CarySwoveland thanks for the support of the basic question; I wasn't sure the other data points would clarify that issue, but I've tried to explain how I'm seeing it now – inman320 Oct 30 '13 at 16:28
  • IMHO `upcase!` shouldn't return nil in the case no changes were made. Since the `!` character almost always means changes in place, the result of doing `a.upcase!` *should* be the same as doing `a = a.upcase`. Which is clearly not the case. – unmultimedio Nov 22 '16 at 17:10

4 Answers4

2

String#upcase!

Upcases the contents of str, returning nil if no changes were made.

'Ab'.upcase! # => "AB"
# nil returned as receiver is already upcased.
"A".upcase! # => nil 
Arup Rakshit
  • 116,827
  • 30
  • 260
  • 317
2

Use upcase, not upcase! for this (upcase! returns nil if string is already upper case):

puts 'A'.upcase
A
mvp
  • 111,019
  • 13
  • 122
  • 148
2

Read this post especially the comments.

Actually it's not that much of a gotcha. Ruby's convention with methods that end with a '!' is that the method would do something surprising. In this case, upcase! changes String instances in place. So you shouldn't depend on the return value nor use it in an assignment.

The convention is:

some_string.upcase!

So upcase! is not supposed to be used in an assignment as it changes the string in place. Rather the use is to check for truth like if str.upcase!. A more intuitive example is gsub!, it returns nil if no substitution was done:

  "abc".gsub!('d','') #=> nil 
tihom
  • 7,923
  • 1
  • 25
  • 29
  • 'Rather the use [of] it to check for truth.' Surely that's not a good idea. When `str = 'A'`, having `if str.upcase! == 'A'` (or `if str.dup.upcase! == 'A'`) would be just a bit confusing. Moreover, I think `a == a.b` is always available to see if a change to `a` would be effected by method `b`. Note also that many non-! methods perform actions or return nil they have no effect, e.g., `[1,2,3].slice(4,5) # => nil`. – Cary Swoveland Oct 26 '13 at 22:07
  • 1
    I meant to check for truth on its own (not comparing to self)....but I agree it is not intuitive. The bang methods are supposed to be dangerous and surprising! – tihom Oct 26 '13 at 22:13
1

According to the documentation: http://ruby-doc.org/core-2.0.0/String.html#method-i-upcase-21

upcase! will return nil if no change was made.

mango
  • 5,577
  • 4
  • 29
  • 41