Why am I getting - "`force_encoding': can't modify frozen String (RuntimeError)"

encoding: ascii-8bit

arr = {
‘1p’ => 1,
‘2p’ => 2,
‘5p’ => 5,
‘10p’ => 10,
‘20p’ => 20,
‘50p’ => 50,
‘£1’ => 100,
‘£2’ => 200
}

arr.keys

=> [“1p”, “2p”, “5p”, “10p”, “20p”, “50p”, “\xC2\xA31”, “\xC2\xA32”]

arr.keys.map{|e| e + ’ test’}

=> [“1p test”,

“2p test”,

“5p test”,

“10p test”,

“20p test”,

“50p test”,

“\xC2\xA31 test”,

“\xC2\xA32 test”]

Q. while the above code does work,why not the below?

arr.keys.map{|e| e.force_encoding(‘utf-8’)} # =>

~> -:14:in `force_encoding’: can’t modify frozen String (RuntimeError)

~> from -:14:in `block in ’

~> from -:14:in `map’

~> from -:14:in `’

Excerpts from Love U Ruby’s message of 2013-11-24 17:26:40 +0100:

  '£2' => 200

“50p test”,

Since Strings are so commonly used as keys and that having mutable
objects as hash keys requires caution, when Hash#[]= is given a string
key it duplicates it, freezes the copy and uses the copy as key. When
you call force_encoding on the key, it fails because it can’t modify a
frozen string. On the other hand, e + ’ test’ doesn’t modify the string
e; rather it creates a new (not frozen) string which is composed by the
content of e and the content of ’ test’.

I hope this helps

Stefano

Stefano C. wrote in post #1128478:

Excerpts from Love U Ruby’s message of 2013-11-24 17:26:40 +0100:

  '£2' => 200

“50p test”,

Since Strings are so commonly used as keys and that having mutable
objects as hash keys requires caution, when Hash#[]= is given a string
key it duplicates it, freezes the copy and uses the copy as key. When
you call force_encoding on the key, it fails because it can’t modify a
frozen string. On the other hand, e + ’ test’ doesn’t modify the string
e; rather it creates a new (not frozen) string which is composed by the
content of e and the content of ’ test’.

Thanks for your point!! Yes I confirmed the same as below :

hsh = {
‘1p’ => 1,
‘2p’ => 2,
‘5p’ => 5,
‘10p’ => 10,
‘20p’ => 20,
‘50p’ => 50,
‘£1’ => 100,
‘£2’ => 200
}

hsh.key(1).object_id # => 70011540
hsh.keys.each_with_object({}){|e,h| h[e]=e.object_id}[‘1p’]

=> 70011540

Actually http://www.ruby-doc.org/core-2.0.0/Hash.html#method-i-keys says
gives us a new array, which I misunderstood something wrong way…

Again thank you very much.