Ruby 3.0 Interpolated Strings Are No Longer Frozen

September 16, 2020

In Ruby, frozen_string_literal: true makes all string literals frozen by default and it helps in reducing needless memory allocations by not creating a new allocation each time a string is redefined.

In Ruby 2.7.1 and earlier versions, interpolated strings were also considered to be frozen when frozen_string_literal: true is used.

# frozen_string_literal: true foo = "Foo" # frozen bar = "Bar" # frozen "#{foo} #{bar}" # frozen

This was intentionally done for simplicity that all strings with "" or '' are frozen by default when frozen_string_literal: true is present.

But an interpolated string is not actually a string literal but rather it can be called as a dynamic string.

Let's see this example:

# Ruby 2.7.1 # frozen_string_literal: true def full_name(first_name, last_name) "Mr. #{first_name} #{last_name}" end fname = "Dummy" lname = "Rik" name = full_name(fname, lname) puts fname.frozen? #=> true puts lname.frozen? #=> true puts name.frozen? #=> true puts name.gsub!(/Mr/, "Mrs") #=> `gsub!': can't modify frozen String: "Mr. Dummy Rik" (FrozenError)

As we can see above, name is an interpolated string i.e dynamic string.
full_name method by default returns a frozen string as Ruby makes interpolated strings frozen by default when frozen_string_literal: true is used.

Let's fix the above FrozenError :

# Ruby 2.7.1 # frozen_string_literal: true def full_name(first_name, last_name) interpolated_string = "Mr. #{first_name} #{last_name}" puts "interpolated_string frozen? : #{interpolated_string.frozen?}" puts "interpolated_string object_id : #{interpolated_string.object_id}" dup_string = interpolated_string.dup puts "dup_string frozen? : #{dup_string.frozen?}" puts "dup_string object_id : #{dup_string.object_id}" dup_string end fname = "Dummy" lname = "Rik" name = full_name(fname, lname) puts name.gsub!(/Mr/, "Mrs") #=> interpolated_string frozen? : true #=> interpolated_string object_id : 60 #=> dup_string frozen? : false #=> dup_string object_id : 80 #=> Mrs. Dummy Rik

We have used dup to return a non freezing string, so that we can use gsub! on it.

But we can see above (interpolated_string object_id : 60, dup_string object_id : 80), we have created two memory allocations unnecessarily.

As said above, we use frozen_string_literal: true to reduce memory allocations but here we are creating extra memory allocation.

Ruby core team has accepted a proposal to not to freeze interpolated strings when frozen_string_literal: true is used and this has been merged to Ruby 3.0 .

Let's for the final time see how our example will shape with Ruby 3.0 :

# Ruby 3.0 # frozen_string_literal: true def full_name(first_name, last_name) "Mr. #{first_name} #{last_name}" end fname = "Dummy" lname = "Rik" name = full_name(fname, lname) puts name.gsub!(/Mr/, "Mrs") #=> Mrs. Dummy Rik
Got something to share with us ? Please inbox

info@scriptday.com

© All rights reserved 2020