Question
In a Ruby file such as this generated rspec binstub, what is the purpose of the magic comment # frozen_string_literal: true?
#!/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 comment intended to do, and how does it affect string literals in Ruby?
Short Answer
By the end of this page, you will understand what # frozen_string_literal: true means in Ruby, how it changes the behavior of string literals, why generated files often include it, and what kinds of errors or benefits it can introduce in everyday code.
Concept
In Ruby, strings are normally mutable by default. That means you can change their contents after they are created.
For example:
name = "Ruby"
name << " on Rails"
# => "Ruby on Rails"
The magic comment:
# frozen_string_literal: true
changes that behavior for string literals in that file.
With it enabled, string literals like:
"hello"
"rspec"
are automatically frozen, which means they cannot be modified.
If you try to change one, Ruby raises an error:
message = "hello"
message << " world"
# FrozenError
Why this matters
This feature helps with two common goals:
- Safety: prevents accidental modification of string literals
- Performance: Ruby can reuse immutable string objects more efficiently in some cases
Important detail
This comment affects string literals written directly in the file. It does not freeze every string object in your entire program.
Mental Model
Think of a string literal as a printed label.
Without # frozen_string_literal: true, Ruby gives you a label written in pencil, so you can erase or add to it.
With # frozen_string_literal: true, Ruby gives you a laminated label. You can read it and pass it around, but you cannot write on it.
So this:
"hello"
is no longer a changeable piece of text in that file. It becomes a read-only value.
That is useful when a string is meant to be a fixed constant like a file path, a key, a command name, or a message template.
Syntax and Examples
The magic comment goes near the top of a Ruby file:
# frozen_string_literal: true
It is usually placed after the shebang line if there is one:
#!/usr/bin/env ruby
# frozen_string_literal: true
Example without frozen string literals
message = "hello"
message << " world"
puts message
Output:
hello world
This works because the string literal is mutable.
Example with frozen string literals
# frozen_string_literal: true
message = "hello"
message << " world"
Output:
FrozenError
Ruby raises an error because "hello" is frozen.
Creating a mutable copy
If you need to modify the string, create a new mutable copy:
Step by Step Execution
Consider this Ruby file:
# frozen_string_literal: true
greeting = "hi"
name = "Sam"
result = greeting + ", " + name
puts result
Step by step
1. Ruby reads the magic comment
# frozen_string_literal: true
Ruby enables frozen string literals for this file.
2. Ruby creates greeting
greeting = "hi"
Because the file has frozen string literals enabled, "hi" is frozen.
3. Ruby creates name
name = "Sam"
"Sam" is also frozen.
4. Ruby builds result
result = greeting + ", " + name
This works. The operator creates a rather than modifying directly.
Real World Use Cases
# frozen_string_literal: true is useful in many real Ruby programs.
Generated files
Tools like Bundler often generate scripts containing many fixed strings:
- gem names
- file paths
- environment variable keys
- command names
Those strings are not meant to be edited at runtime, so freezing them is sensible.
Libraries and gems
Library code often uses many constant-like strings:
JSON_CONTENT_TYPE = "application/json"
Freezing literals helps avoid accidental mutation.
Rails apps
Rails applications may use many strings for:
- parameter keys
- route names
- SQL fragments
- logging messages
- configuration values
Freezing literals can reduce accidental bugs and encourage safer string handling.
CLI scripts
Command-line tools often define fixed option names and messages:
puts "Usage: my_tool [options]"
Those strings are ideal candidates for freezing.
Performance-sensitive code
In code that creates many repeated string literals, immutability can reduce unnecessary object mutation and sometimes improve memory behavior.
Real Codebase Usage
In real projects, developers use frozen string literals as part of a broader style of writing safer Ruby.
Common pattern: immutable defaults
# frozen_string_literal: true
DEFAULT_ROLE = "guest"
STATUS_OK = "ok"
These values are intended to stay unchanged.
Common pattern: duplicate before mutation
If a value starts from a literal but needs editing, duplicate it first:
# frozen_string_literal: true
path = "users/".dup
path << "123"
Common pattern: use interpolation or + for new strings
Instead of mutating an existing literal:
full_name = first_name + " " + last_name
or:
full_name = "#{first_name} #{last_name}"
These create new strings rather than changing a frozen literal.
Validation and error messages
Developers often store fixed messages safely:
Common Mistakes
Mistake 1: Thinking all strings become frozen
This comment does not freeze every string in Ruby.
# frozen_string_literal: true
a = "hello"
b = String.new("hello")
puts a.frozen? # true
puts b.frozen? # false
Mistake 2: Trying to append to a literal directly
Broken code:
# frozen_string_literal: true
text = "abc"
text << "def"
This raises FrozenError.
Fix it like this:
# frozen_string_literal: true
text = "abc".dup
text << "def"
Mistake 3: Confusing + with <<
+ creates a new string. << mutates the existing string.
# frozen_string_literal: true
a =
b = a +
Comparisons
| Concept | What it does | Mutable? | Scope |
|---|---|---|---|
| Normal string literal | "hello" creates a regular string literal | Usually yes | Current object |
# frozen_string_literal: true | Freezes string literals in the file | No for literals in that file | Current file |
"hello".freeze | Freezes one specific string object | No | That object only |
String.new("hello") | Creates a new string object | Yes by default | That object only |
+ with strings | Creates a new combined string |
Cheat Sheet
# At the top of a file
# frozen_string_literal: true
What it means
- Freezes string literals in that Ruby file
- Helps prevent accidental mutation
- Can improve memory/performance in some cases
- Applies per file, not globally
Affected
# frozen_string_literal: true
s = "hello"
s.frozen? # => true
Not automatically affected
# frozen_string_literal: true
s = String.new("hello")
s.frozen? # => false
Safe operations on frozen strings
name = "Ruby"
name.upcase # returns a new string
name + "!" # returns a new string
"#{name} rocks" # creates a new string
Operations that fail
name = "Ruby"
name << "!" # FrozenError
name.gsub!(, )
FAQ
What does # frozen_string_literal: true do in Ruby?
It tells Ruby to freeze string literals in that file, making them immutable.
Does it freeze every string in my program?
No. It only affects string literals in the current file.
Why do Bundler or generated files include it?
Generated files often contain many fixed strings that should not be modified, so freezing them is safe and useful.
What error happens if I modify a frozen string?
Ruby raises FrozenError.
Can I still combine frozen strings?
Yes. Operations like + and interpolation create new strings, so they still work.
How do I make a frozen string editable?
Use dup to create a mutable copy:
editable = "hello".dup
Is this the same as calling .freeze everywhere?
It has a similar effect for literals, but the magic comment applies automatically to all string literals in that file.
Should I always use # frozen_string_literal: true?
It is common and helpful in many Ruby codebases, but you should understand that code mutating literals may need to be updated first.
Mini Project
Description
Create a small Ruby script that builds status messages for a command-line app while using # frozen_string_literal: true. This project demonstrates the difference between reading fixed string literals and trying to mutate them. It also shows how to correctly create editable copies when needed.
Goal
Build a Ruby script that uses frozen string literals safely and produces formatted output without raising FrozenError.
Requirements
- Add
# frozen_string_literal: trueat the top of the file. - Define at least two fixed string literals for labels or message prefixes.
- Build a final message using safe string operations.
- Create one mutable copy with
dupand modify it. - Print the final results to the console.
Keep learning
Related questions
Calling a Class Method from an Instance in Ruby
Learn how to call a class method from an instance in Ruby using self.class, with examples, pitfalls, and practical usage patterns.
Calling an Overridden Monkey-Patched Method in Ruby
Learn how to call the original method when monkey patching in Ruby, including alias_method patterns, examples, pitfalls, and practical usage.
Convert a Unix Timestamp to Ruby DateTime
Learn how to convert Unix timestamps to Ruby DateTime and Time objects, with examples, differences, pitfalls, and practical Ruby usage.