Contents
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.
- Being this the first step into immutable objects in Ruby, this could be seen as a small change but it could serve as the foundation of a greater one.
- 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.
- There were a few years when folks were sprinkling .freeze calls on all the string literals for the performance benefits.
- It was a nightmare to migrate our 1.8.7 codes to 1.9, because of many small incompatiblity in the release.
- This application runs on Heroku, rolling back with really easy and does not require reverting the code back and redeploying.
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’ 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 are Strings (and String Literals)?
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.
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.
By freezing string literals, your program will raise an exception when trying to modify the string literal.
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 Ethical Hacking Tutorials 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.
We keep the cop enabled, but never freeze interpolated strings. It is usually not useful, unless we anticipate that certain variants are instantiated very frequently per-process. There were originally some code fragments in RDoc assuming that a string is mutable. To enable frozen-string-literal, they were changed, which brought performance degradation. After that, enabling frozen-string-literal made RDoc a bit faster, but the net result was worse than the original. Want to use frozen strings; with the toplevel comment overriding this anyway.
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.
‘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.
Comments
We have had the syntactic hacks (.freeze etc), frozen-string-literal pragma, and the command-line global flag for many years now. A very large corpus of existing code already opts into frozen string literals today, including most of Rails and its dependencies. RuboCop and other tools have been recommending users include the frozen-string-literal pragma as well. MRI could record whether a frozen string was explicitly frozen by the code. This would give some warnings until all cases are fixed and the switch could be then turned off for applications which fixed all cases… If you’re preparing for the upcoming Ruby 2.3 release, you’re in the right place.
I’d love to see some sort of warning happen in 2.7…perhaps a flag you can pass or just a new warning that recommends alternative code or the appropriate pragma. If the performance improvement can be discarded, even the addition is unnecessary. It is said that 90% of the execution time of a computer program is spent executing 10% of the code. Making 100% of the code immutable resembles “premature optimization is the root of all evil”. The magic comment can be now accepted as a migration path. If you’re lucky enough to have a situation where all of your dependencies are frozen-string-literal friendly, then you can just use the RUBYOPT environment variable.
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 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.
I need to make sure that the option will be guaranteed to remain exist in future versions. This effect can win the small code bloat with dup (or String.new or something new). We will need to choose “foo” and “foo”.dup instead. Matz said “All String literals are immutable on Ruby 3”.
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.
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 Documenting Python Code 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.
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 .
Same value literals points to the same object
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.
Code Comments¶ ↑
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 7 Best Online Course to Learn Programming & Coding in 2022 by javinpaul Javarevisited 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?
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.