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:
- It's fine for String#force_encodingto be a non-bang method. This is not completely inconsistent with other conventions in the language.
- If it were a bang method, it would require a non-bang counterpart for consistency.
- I, personally, don't think the use case for a non-mutating String#force_encodingis common enough to warrant its creation. In the rare case that you actually wanted to do this, you could write:string.dup.force_encoding(...).