Contents
Mike documented his use of the magic comment and how it improved his use and helped cleanup his code. Finally, let’s measure how allocating less objects therefore running garbage collection less often impacts on performance. Note that the cop will accept files where the comment exists but is set to false instead of true. Creating a unfrozen string from a frozen string assigned to a variable also showed similar timing. When using the + operator, one may need to surround it with parens (like (+str)). Re the magic comment, a lot of Ruby code uses it, and it can be set globally via RUBYOPT.

First, it’s important to note that this change is just the first step towards all string literals becoming immutable in Ruby 3. String literals are simply strings that are created using Ruby’s built-in string syntax. This includes those created using double quotes (“), single quotes (‘), or the special forms %Q and %q. Traditionally, these forms have all created strings that can be modified.
encoding Directive¶ ↑
Since we wrote this post –enable-frozen-string-literal-debug has been renamed and its behavior has changed. First, we had to enabled it to know where string literals were being created. On Feature #11725 implementation, the original flag was renamed to –debug-frozen-string-literal. Furthermore, if we try to mutate a statically created string literal, the error message will show by default where the string was created.

That’s the only way to keep backwards compatibility, right? Ruby might introduce some other method to get the information whether the string is still in the default initial frozen state though… One possible option would be to have „“ be a mutable string, and “ be an immutable string. It doesn’t make sense to have „“ strings be immutable if they contain interpolation, since they can’t be deduped, and “ strings can’t contain interpolation. Beyond the actual usage changes, it’s probably worth adding in the pragma comment across all of your files. That may initially seem like hard work, but you can enforce this via RuboCop, and add them to existing projects with pragmater.
How about, say, correctness as seen in immutable state functional languages, or hybrid functional/imperative languages which are immutable-by-default like Rust. Freezing an object doesn’t protect the reference from being completely re-assigned, but I think that is less likely to happen by accident. In the case of a constant, re-assigning also produces a warning, while mutating doesn’t. But the constants within the frozen submodule cannot be changed themselves. We might consider moving this cop to rubocop-performance, though, if there’s no chance that this will become the default in Ruby at some point (e.g. Ruby 4, etc). I’m still against frozen-string-literal by default.
Alternative ways to freeze a String
For example, the following code could be used to create a string containing a greeting, and then to modify the greeting to make it more personal if a name is available. But, if you woke up today feeling like a kamikaze, enable frozen strings for the whole project and prepare yourself for some serious bug hunting. So, to enable string immutability for a project add the following flag when running your app.
Oh, now I got it, sorry for the confusion and thanks for explaining. I didn’t pay attention that the performance gains were due to saving allocations by sharing the same buffer everytime the same string is built. In that case, sorry, but I have no idea on how to keep backwards compatibility while still taking advantage of the performance benefits.
In the next section let’s take a look how this impacts on performance. It is my understanding that freeze allows Ruby to lock a String to the original text in the source code instead of creating a separate buffer as needed for a mutable object. Less experienced developers, or developers without Ruby experience, could accidentally think the string is passed as a value, not a reference. I guess the interaction between the „false“ state and the current runtime default is what has me confused. I REALLY like the idea but I am sure introducing this could cause HUGE compatibility issue, even bigger than Ruby 1.9. So I officially abandon making frozen-string-literals default .
This mode is “experimental” and has not been discussed thoroughly. This will be revisited before Ruby 3.1 to either allow ‘copy` or to instead remove this mode. It must appear in Best web development tools of 2022 the first comment section of a file. This process cannot be reversed – once an object is frozen in Ruby, it will remain frozen. The comment must be on the first line of the file.
- Given the low overhead of having to call ‚dup‘ when we want an unfrozen string, this is certainly a tradeoff worth making.
- The returned String will be deduplicated if it has no instance variables.
- That’s why the location of the string literal creation is added to the error message, helping you to find where the issue is.
- I had one relatively small application that had an API component I was optimizing (Rails 5.2.x and Ruby 2.6.6).
- Currently string immutability is included as opt-in, being the first step of a multi stage rollout and if it works well it will beenabled by default on Ruby 3.0.
In fact, Ruby 2.3 will hold a unique instance for each string literal value used in the program. This means that ‘a’ and ‘a’ references the same object. I’m a supporter of the idea of making strings frozen by default. Maybe some syntax for unfrozen strings like ‚unfrozen’u might be a good idea rather How to Hire an App Developer for Your Business than using dup, but I’m not really worried about this. I’m no benchmarking expert, but some quick tests locally have suggested that using frozen string literals does make code using a lot of literals a bit faster. The next morning in fired up Skylight to check the response times from the API.
What will change for Ruby developers?
If you do mutate, use String.new to allocate a mutable String instead of “”. It makes the code uglier and needs to be called everywhere you declare a String. On the table below you can find the results obtained from running the benchmark without and with frozen literals respectively. Despite it’s highly unlike that could happen, it could happen.
To me, it also looks less ugly than a magic comment. To prevent this type of bug, why is it sufficient to freeze only literals? String#+ also must return a frozen String, I think. This issue tries to match performance and usability . Why do matz and akr want to make String literals immutable, at first?

Another way would be to run the test suites of a set of gems and see how much breaks with –enable-frozen-string-literal, including gems not using the pragma. Performance should indeed be a concern in my opinion. Just because there are other faster languages it doesn’t mean performance should not be a priority for Ruby.
mutable without interpolation¶
‚a‘ the interpreter creates two objects with the value ‘a’, hence equal? With immutable strings this is no longer true, in this case the interpreter will use a reference to the same object, then as each reference points to the same object equal? As it goes with most updates, there will be some short term pain as Ruby changes to make string literals immutable by default. The good news is that it will happen gradually, and it won’t be difficult to detect and fix existing code that’s affected by this change. In return, we’re going to get a version of Ruby that uses less memory and thus runs a bit quicker. Given the low overhead of having to call ‚dup‘ when we want an unfrozen string, this is certainly a tradeoff worth making.
To my surprise, the response times did not improve. Since the only change in this deploy was the use of magic comments, I decided to rollback the changes. This application runs on Heroku, rolling back with really easy and does not require reverting the code back and redeploying. It improves application performance by not allocating new space for the same string, thereby also saving time for garbage collection chores.
If an argument is passed, the new string will respect the encoding of the first argument – which is likely to be Ruby’s default of UTF-8. Allocating just a single object for both ‚a‘ SQL Server DBA job description template and ‚b‘ reduces the amount of memory used and this, in turn, reduces pressure on the garbage collector. Matz (Ruby’s creator) decided to make all String literals frozen by default.
What about the benefit of making code easier to reason about by preventing accidental modification? Well, that benefit is already there, you just need to use it. You can call ‚freeze‘ on any object and it will prevent modification.