On upcoming immutable string literals in Ruby

Today I checked one of the solutions made by our Junior Rails Developer class student. As part of the course they make Rails apps but also learn from smaller code examples delivered by exercism.io.

I found there an opportunity for him to learn more about a few things…

That’s his code which inspired me for a reflection.

string = ''
string += 'Pling' if num % 3 == 0
string += 'Plang' if num % 5 == 0
string += 'Plong' if num % 7 == 0
string += num.to_s if string.empty?
string

I recommended reading about #tap; how one could use a Hash to remove some duplication. I also thought it could be a good occasion to talk about how Ruby String’s are mutable but in some time string literals will be immutable . The keyword here is literals.

Let’s focus on a very small part of the code:

string = ''
string += 'Pling' if num % 3 == 0

We could easily refactor it to:

string = ''
string << 'Pling' if num % 3 == 0

But not when string literals are enabled to be immutable.

rvm use ruby-2.3.0
RUBYOPT=--enable-frozen-string-literal irb
s = ""

s.frozen?
# => true 

s << "asd"
# RuntimeError: can't modify frozen String

Obviously, because s = "" is a string literal.

In such case we would need to go with a less elegant solution probably:

string = String.new
string << 'Pling' if num % 3 == 0

because

s = String.new

s.frozen?
# => false

s << "asd"
# => "asd"

So just be aware that in upcoming Ruby versions s = "" and s = String.new might not be equal. And in the case when you are building a new string via multiple transformations or concatenations the 2nd version might be preffered.

I wonder if some time later ruby (4 ?) will make all Strings immutable (not only those created via literals) and introduce StringBuilder class like .Net or Java has?

Happy 2017!