LaunchKit
All quizzes
Ruby 0 views

chomp! returns nil when nothing changes

Question

What's the return value of the following Ruby 3 code?

str = "hello\n"
str.chomp!.reverse # => "olleh"

str = "hello"
str.chomp!.reverse # => ???

The correct answer is

Explanation

TL;DR

String#chomp! removes a trailing record separator in place and returns the string, but when there is nothing to remove it returns nil. "hello" has no trailing newline, so str.chomp! returns nil and the chained .reverse becomes nil.reverse, which raises NoMethodError.

Step by step

The first pair works because the string actually changes:

str = "hello\n"

str.chomp!  # => "hello"
str         # => "hello"

chomp! strips the \n, mutates str in place, and returns the mutated string, so .reverse has a String receiver.

The second pair does not:

str = "hello"

str.chomp!  # => nil

No trailing separator, no modification, and by convention chomp! signals "nothing changed" by returning nil. The chain then blows up:

str = "hello"

begin
  str.chomp!.reverse
rescue NoMethodError => e
  e.message # => "undefined method 'reverse' for nil"
end

The bang convention

This is not a chomp! quirk. Many in-place String and Array mutators return nil when they made no change: chop!, strip!, squeeze!, gsub!, sub!, upcase!, downcase!, uniq!, compact!, flatten!. The nil is a useful "did anything happen?" signal in a conditional, but it makes these methods unsafe to chain.

When you need chaining, use the non-bang version, which always returns a String:

"hello".chomp.reverse    # => "olleh"
"hello\n".chomp.reverse  # => "olleh"

chomp returns a new string whether or not it removed anything, so the chain is safe in both cases.

Share this quiz

Comments

No comments yet. Be the first.

Only used to confirm and publish your comment. Never shown publicly, never shared.

Markdown: **bold**, `code`, ```fenced blocks```, > quotes, [links](url). HTML and images are not rendered.