-2

I'm just wondering if the String method force_encoding, since it changes the encoding of the given string, and not a copy of it, rather should be named force_encoding!

Update to clarify what kind of answer's I'm after: Is there some convention or general idiosyncracy of ruby which I am missing here, which may explain the seemingly inconsistent naming?

Magne
  • 16,401
  • 10
  • 68
  • 88
  • 3
    I doubt it’s a good question for SO. Maybe you’d better ask on some ruby forum. – Aleksei Matiushkin Jan 04 '18 at 13:51
  • 1
    There are several non-bang methods which modify the receiver: `String#clear`, `String#concat`, `String#delete`, `String#insert`, `String#replace`, `String#setbyte` – Stefan Jan 04 '18 at 14:11
  • 1
    ...While I'm here, let's also highlight the *other* common misconception: predicate methods (i.e. ending in `?`) *usually, but do not always* mean "returns `true` or `false`"). Technically, they just mean "returns truthy or falsey". For example, `File.size?` either returns the file size (an integer) or `nil` (if the file does not exist). – Tom Lord Jan 04 '18 at 14:19

2 Answers2

5

Contrary to popular belief, and many misleading beginners' ruby guides, bang methods do not mean "this method mutates the object".

That's usually the case, but not always.

A better interpretation of the convention for bang methods is "this is a more dangerous version of its non-bang counterpart".

For example:

  • String#gsub! is the "dangerous" version of String#gsub, since it mutates the object.
  • ActiveRecord::Base#save! is the "dangerous" version of ActiveRecord::Base#save, since it raises an exception if validation fails.
  • exit! is the "dangerous" version of exit, since the former exits immediately, whereas the latter actually raises an exception - which could be rescued elsewhere in the code.

Furthermore, note that there are plenty of ruby methods that do mutate the object but aren't bang methods. For example, String#delete and Array#pop.

This article goes into more detail on the matter.


So, (warning: opinionated response!) I would argue that:

  1. It's fine for String#force_encoding to be a non-bang method. This is not completely inconsistent with other conventions in the language.
  2. If it were a bang method, it would require a non-bang counterpart for consistency.
  3. I, personally, don't think the use case for a non-mutating String#force_encoding is common enough to warrant its creation. In the rare case that you actually wanted to do this, you could write: string.dup.force_encoding(...).
Tom Lord
  • 27,404
  • 4
  • 50
  • 77
  • ...And if you disagree with my opinion about point 3, why not raise a feature request for a future ruby version? It could also be a fun/simple task to write a ruby gem that changes the language behaviour... But I suspect doing so would break all sorts of things in a large project! Maybe it could be implemented as a refinement. – Tom Lord Jan 04 '18 at 14:12
  • Thanks a lot! This was the kind of answer I was hoping to get. As I suspected there might be a subtle reason behind it. And clearing up the misconception was really helpful. TIL something new. :) – Magne Jan 04 '18 at 14:48
2

Despite the common assumption, bang methods do not necessarily mutate their receiver, nor can you assume that non-bang methods don't mutate their receiver. Examples for that are e.g. Array#pop, Array#clear.

Instead, methods with a trailing bang indicate that the method is "dangerous" in some way. Especially in the Ruby core, it is generally expected that for each bang method there is always a less dangerous non-bang method.

See the answers to a more generic question on that topic for more details: Why are exclamation marks used in Ruby methods?

Holger Just
  • 52,918
  • 14
  • 115
  • 123