+= operator appears to modify frozen string
Nothing is modifying your frozen String
You are re-assigning a
to a new String
with
a += "this string"
which is internally the same in Ruby as
a = a + "this string"
When you add two String objects in Ruby, it will create a new String containing the result (this is normal behaviour for +
operator on most objects that support it). That leaves the original "Test" and "this string" values unchanged. The original, frozen String (containing "Test") will remain in memory until it is garbage collected. It can be collected because you have lost all references to it.
If you attempted to modify the object in place like this:
a << "this string"
then you should see an error message RuntimeError: can't modify frozen String
Basically, you have confused a
, the local variable, with the String
object to which it is pointing. Local variables can be re-assigned at any time, independently of the objects stored by Ruby. You can verify this is what has happened in your case by inspecting a.object_id
before and after your a +=
... line.
Getting can't modify frozen string when using string.insert
If the passed argument number
is already a frozen string to start with, then number = number.to_s
won't change a thing and you won't be able to modify it in place (with number.insert
):
add_zeros("24".freeze, 10) # => TypeError: can't modify frozen string
Creating a new string from it ("0#{number}"
) is not a problem, of course.
The reason why your string is frozen is subtle. When you use a string as a hash key, Ruby will make a copy of it and freeze it:
s = "hello"
h = {}
h[s] = :world
key = h.keys.first
key.equal?(s) # => false (Ruby made a copy)
key.frozen? # => true (Ruby automatically freezes the copy)
Anyways, as a general rule, a method should not modify its arguments.
In this case, you probably want to use rjust
:
24.to_s.rjust(10, "0") # => "0000000024"
Ruby - can't modify frozen string (TypeError)
since google took too long to find the right answer ...
needed to do
arg_dup = ARGV[ 0 ].dup
Freezing variables in Ruby doesn't work
You freeze objects, not variables, i.e. you can't update a frozen object but you can assign a new object to the same variable. Consider this:
a = [1,2,3]
a.freeze
a << 4
# RuntimeError: can't modify frozen Array
# `b` and `a` references the same frozen object
b = a
b << 4
# RuntimeError: can't modify frozen Array
# You can replace the object referenced by `a` with an unfrozen one
a = [4, 5, 6]
a << 7
# => [4, 5, 6, 7]
As an aside: it is quite useless to freeze Fixnum
s, since they are immutable objects.
Ruby: What does the comment frozen_string_literal: true do?
# 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
.
The comment must be on the first line of the file.
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
Why instance variables can be modified through a field reader with the operator?
Your @word
references an object in memory. This object happens to be a Hello!
string. Your class does not allow to (easily) set a new object for your @word
instance variable, but nothing currently stops you from modifying the object directly. If you wish for the object to not be modifiable, you could .freeze
it:
class Test
attr_reader :word
def initialize(word)
@word = word.freeze
end
def append_word(token)
word << token
end
end
> Test.new("Hello").append_word("world!")
# FrozenError (can't modify frozen String)
What does the - mean in front of a ruby symbol?
That's String#-@
:
Returns a frozen, possibly pre-existing copy of the string.
Example:
a = "foo"
b = "foo"
a.object_id #=> 6980
b.object_id #=> 7000
vs:
a = -"foo"
b = -"foo"
a.object_id #=> 6980
b.object_id #=> 6980
Related Topics
Rails 3 Caching: Expire Action for Named Route
Ruby on Rails: Radio Buttons for Collection Select
Net-Ssh and Remote Environment
Rails - Multi Tenant Application with Customization Framework
Project Euler #3 in Ruby Solution Times Out
Differencebetween Unicorn and Unicorn_Rails
Ruby Strftime '%Z' Method Returns '0545' Instead of 'Npt'
Why Does 6.Times.Map Work in Ruby 1.8.7 But Not 1.8.6
Single Quote String String Interpolation
How to Set Up the Recipient Id in Public Activity
Uninstall Ruby on Rails on MAC Os X 10.6
Is There a More Concise Way to Call an Outside Method on a Map in Ruby
How to Use H2 as Embedded Database in Postgres-Compat Mode, from Jruby/Rails
Can't Setup Ruby Environment - Installing Fii Gem Error
How to Get Today's Date in Ruby 1.9.3
Given a Url, How to Get Just the Domain
Ssl_Connect Returned=1 Errno=0 State=Sslv3 Read Server Certificate B: Certificate Verify Failed MAC