395

This is the rspec binstub in my project directory.

#!/usr/bin/env ruby
begin
  load File.expand_path("../spring", __FILE__)
rescue LoadError
end
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
# The application 'rspec' is installed as part of a gem, and
# this file is here to facilitate running it.
#

require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
  Pathname.new(__FILE__).realpath)

require "rubygems"
require "bundler/setup"

load Gem.bin_path("rspec-core", "rspec")

What is this intended to do?

# frozen_string_literal: true
Braiam
  • 1
  • 11
  • 47
  • 78
messanjah
  • 8,977
  • 4
  • 27
  • 40

3 Answers3

514

# frozen_string_literal: true is a magic comment, supported for the first time in Ruby 2.3, that tells Ruby that all string literals in the file are implicitly frozen, as if #freeze had been called on each of them. That is, if a string literal is defined in a file with this comment, and you call a method on that string which modifies it, such as <<, you'll get RuntimeError: can't modify frozen String.

Freezing strings prevents bugs caused by accidentally modifying a string when you didn't intend to, and may improve performance.

Like any magic comment, the frozen_string_literal comment must be in the first comment section of the file. Ironically, the frozen_string_literal comment in that binstub is not in the binstub's first comment section and will be ignored.

In Ruby 2.3, you can use this magic comment to prepare for frozen string literals being the default in Ruby 3.

In Ruby 2.3 run with the --enable=frozen-string-literal flag, and in Ruby 3, string literals are frozen in all files. You can override the global setting with # frozen_string_literal: false.

If you want a string literal to be mutable regardless of the global or per-file setting, you can prefix it with the unary + operator (being careful with operator precedence) or call .dup on it:

# frozen_string_literal: true
"".frozen?
=> true
(+"").frozen?
=> false
"".dup.frozen?
=> false

You can also freeze a mutable (unfrozen) string with unary -.

Source: magic_comment defined in ruby/ruby

Dave Schweisguth
  • 36,475
  • 10
  • 98
  • 121
  • 55
    Important thing to notice regarding freezing strings is that it [improves app's performance](https://bugs.ruby-lang.org/issues/8976#note-30). See also [here](https://www.lucascaton.com.br/2016/01/19/what-is-frozen_string_literal-in-ruby/) – Andres Jun 29 '17 at 17:11
  • 2
    @dave-schweisguth Shouldn't we expect `-"foo"` to be the same as `"foo".freeze`? When I check `(-"foo").__id__` I get a different value each time, but `"foo".freeze.__id__` is same each time. Any ideas? – lilole Aug 16 '17 at 23:10
  • I wonder if this function is the issue, it seems to only be called with the unary minus. https://github.com/ruby/ruby/blob/trunk/string.c#L2572 – lilole Aug 17 '17 at 00:40
  • [`#freeze`](https://github.com/ruby/ruby/blob/trunk/string.c#L2541) is implemented mostly as it is for all `Object`s, but unary `-` is specific to `String`. But I don't know why unary `-` doesn't just call `#freeze`. – Dave Schweisguth Aug 19 '17 at 15:55
  • 4
    `-` is for deduplicating the String to save memory, in addition to returning a frozen String. – eregon Feb 28 '18 at 10:11
  • 22
    While you still can use the magic comment, Matz officially decided not to make all string literals immutable by default in Ruby 3: https://bugs.ruby-lang.org/issues/11473#note-53 – Konstantin Tikhonov May 16 '19 at 18:25
  • 2
    I still don't get it. Why is it needed? – Eddie Jul 08 '19 at 15:02
  • 3
    @Eddie because most languages treat strings as immutable, which helps ensure you don't accidentally modify them and can improve performance. Fewer state changes in a program mean less complexity. It's better to opt-in to mutability after careful consideration rather than making everything mutable by default. [What is immutability and why should I worry about it?](https://stackoverflow.com/questions/622664/what-is-immutability-and-why-should-i-worry-about-it) may help. – ggorlen Dec 26 '22 at 05:51
  • 1
    Can adding this to legacy codebase break something? What is the likelyhood? – lacostenycoder Feb 02 '23 at 22:42
  • is `The comment must be on the first line of the file.` true? the example here does NOT have it on the first line, so does it not work then? which line should go first in an executable ruby script: freezing string literals or the shebang for executability? – gerry3 Apr 06 '23 at 22:31
  • @gerry3 good observation; I corrected my answer. Note however that I'm pretty sure that the Ruby interpreter doesn't see the hashbang line. The shell should remove it before sending the rest of the file through the executable that it specifies. – Dave Schweisguth Apr 07 '23 at 00:44
218

It improves application performance by not allocating new space for the same string, thereby also saving time for garbage collection chores. How? when you freeze a string literal(string object), you're telling Ruby to not let any of your programs modify the string literal (object).

Some obvious observations to keep in mind.

1. By freezing string literals, you're not allocating new memory space for it.

Example:

Without magic comment allocates new space for the same string (Observe the different object IDs printed)

def hello_id
  a = 'hello'
  a.object_id
end

puts hello_id   #=> 70244568358640
puts hello_id   #=> 70244568358500

With magic comment, ruby allocates space only once

# frozen_string_literal: true

def hello_id
  a = 'hello'
  a.object_id
end

puts hello_id   #=> 70244568358640
puts hello_id   #=> 70244568358640

2. By freezing string literals, your program will raise an exception when trying to modify the string literal.

Example:

Without magic comment, you can modify the string literals.

name = 'Johny'
name << ' Cash'

puts name     #=> Johny Cash

With magic comment, an exception will be raised when you modify string literals

# frozen_string_literal: true

name = 'john'
name << ' cash'  #=> `<main>': can't modify frozen String (FrozenError)

puts name      

There's always more to learn and be flexible:

Tenzin Chemi
  • 5,101
  • 2
  • 27
  • 33
52

In Ruby 3.0. Matz (Ruby’s creator) decided to make all String literals frozen by default.

EDIT 2019: he decided to abandon the idea of making frozen-string-literals default for Ruby 3.0 (source: https://bugs.ruby-lang.org/issues/11473#note-53)


You can use in Ruby 2.x. Just add this comment in the first line of your files.

# frozen_string_literal: true

The above comment at top of a file changes semantics of static string literals in the file. The static string literals will be frozen and always returns same object. (The semantics of dynamic string literals is not changed.)

This way has following benefits:

No ugly f-suffix. No syntax error on older Ruby. We need only a line for each file.

Plese, read this topic for more information.

https://bugs.ruby-lang.org/issues/8976

MrYoshiji
  • 54,334
  • 13
  • 124
  • 117
AlexSh
  • 1,468
  • 15
  • 12
  • 1
    Unfortunately this comment does not work for strings in arrays, so they still need to be freezed explicitly – ToTenMilan Feb 21 '19 at 12:19
  • 2
    @ToTenMilan In Ruby 3.0.6, the frozen_string_literal comment does work for strings in arrays, the strings cannot be modified. What Ruby version did you find this did not work in? – Eliot Sykes May 30 '23 at 09:43