Home > Mobile >  Meaning of Ruby methods ending with "!"
Meaning of Ruby methods ending with "!"

Time:09-16

I'm a newbie to Ruby, and so far have found the explanations for ! a bit too technical.

Let's say we have the following:

print "What is your first name?"

first_name = gets.chomp.upcase!

puts "Hi, #{first_name}. How are you?"

What does the ! add to the method? Does it mean that every time I use #{first_name} in any subsequent strings, all the letters in the name will print in upper-case? How is the code affected if I leave ! out?

Please explain it in layman's terms if you can, as I'm still coming to grips with some of Ruby's technical jargon.

Thanks in advance.

CodePudding user response:

Ruby method names may end in !. It doesn't do anything special by itself, but there is a convention on which methods have it (though it is not always consistent); thus, just learn the method String#upcase! (and how it differs from String#upcase) on individual method-by-method basis, with the convention being a helpful reminder.

The convention is that the methods ending in ! are either dangerous, or change the object, or perform some other kind of change, or can raise an error where the other version wouldn't.

Specifically, String#upcase! changes the string it operates on. String#upcase makes a new string that is upcased.

foo = "test"
p(foo.upcase)  # => "TEST"
p(foo)         # => "test"

foo = "test"
p(foo.upcase!) # => "TEST"
p(foo)         # => "TEST"    ​

As described in comments, String#upcase! returns nil when no upcasing needs to happen, so you never want to reassign the return value of String#upcase! back to the same variable you tried to upcase!.

foo = "TEST"
p(foo.upcase!) # => nil
p(foo)         # => "TEST"    ​

RE: bang methods: see here

EDIT: "receiver" is the object receiving the message (i.e. method call). In foo.upcase!, the object in foo is the receiver. In "TEST".upcase, the string "TEST" is the receiver.

"in-place"` means the object itself is changed. The alternative is to create a new object that has the changes you want applied, with no change to the original object. For example, if you have a photo, cover the bottom half with a piece of paper and take a photo of that, you did a crude censorship by creating a new photo that is missing the lower half (but the original still exists). If you splat paint on the original photo, you censored it "in place".

CodePudding user response:

What does the ! add to the method?

Nothing. The ! is simply part of the message name, just like the u, the p, the c, the a, the s, and the e are. It does not change anything about the message.

How is the code affected if I leave ! out?

Then you are simply sending a different message.

There is absolutely no difference whatsoever between upcase and upcase! or between foo and bar. They are just message names. If you write foo, then you send message foo, if you write bar, then you send message bar, if you write upcase, then you send message upcase (which in your case will be dispatched to the method String#upcase), if you write upcase!, then you send message upcase! (which in your case will be dispatched to the method String#upcase!).

There is absolutely nothing special about message names ending in !, just like there is absolutely nothing special about message names ending in ?, just like there is absolutely nothing special about message names ending in a or b or c.

There are, however, some conventions. In particular, the convention is:

If you have a pair of methods that both do more or less the same thing, then the one that is more surprising is named with a !.

That's it. That is all there is to it.

For example, there are the methods Process::exit and Process::exit!. They both do the same thing (terminate the process), but one of them skips running the installed exit handlers.

Since they both do the same thing, they could both be named "exit", but of course, you can't have two methods with the same name (on the same receiver). Therefore, we need to somehow distinguish between the two. We could come up with a completely different name for one of the two, but that would also be annoying.

So, instead, we add a ! to one of the two methods. Now, the question is: which one of the two do we add the ! to? Based on our convention, the "more surprising" one should get the !. The Ruby developers chose the one which ignores the exit handlers, which I think is the right choice: the whole point of exit handlers is that they get run when you exit, so a method that exits without running the exit handlers is surprising, and therefore should be marked with a !.

Note that none of this has anything to do with "destructive" or "mutation". Process::exit! doesn't mutate anything and it doesn't destroy anything. Note also that we only use the ! to mark the more surprising method of a pair of methods. There should never be a method named foo! when there is not also a method named foo.

In particular, the fact that a method does not have a ! does not tell you anything about the method. And the only thing that a method that does have a ! tells you is that there are two methods, and you should probably check the documentation to make sure that you understand what these two methods do and what the "surprise" is that the ! is warning you about. That's it.

  • Related