+= Operator Appears to Modify Frozen String

+= 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 Fixnums, 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



Leave a reply



Submit