Ruby で pack をしたら、 can't modify frozen String
が出たので、記録する。
[47] pry(main)> str = 'abc'
=> "abc"
[48] pry(main)> str.freeze
=> "abc"
[49] pry(main)> [str].pack('p*')
RuntimeError: can't modify frozen String
from (pry):48:in `pack'
pack
は変更(modify
)ではない、エラーが出るのが不思議だった。
そこで思いついたのは、「ポインタを渡すと、破壊できるから」という理由。
本当にそうなのかを調べようと思ったが、 2.6.3 まではエラーだが、 2.7.3 からはエラーにならなくなっていることに気づいた。
下記のコードを tmp.rb
として保存し、手元にある version を切り替えながらテストした。
# tmp.rb
# frozen_string_literal: true
puts RUBY_VERSION
['abc'].pack('p*')
$ rbenv shell 2.3.3
$ ruby tmp.rb
2.3.3
tmp.rb:5:in `pack': can't modify frozen String (RuntimeError)
from tmp.rb:5:in `<main>'
$ rbenv shell 2.5.0
$ ruby tmp.rb
2.5.0
Traceback (most recent call last):
1: from tmp.rb:5:in `<main>'
tmp.rb:5:in `pack': can't modify frozen String (FrozenError)
$ rbenv shell 2.6.3
$ ruby tmp.rb
2.6.3
Traceback (most recent call last):
1: from tmp.rb:5:in `<main>'
tmp.rb:5:in `pack': can't modify frozen String (FrozenError)
$ rbenv shell 2.7.3
$ ruby tmp.rb
2.7.3
$ rbenv shell 3.0.1
$ ruby tmp.rb
3.0.1
ポインタが関係なかったのは残念。
バージョンを上げれば解決する。
バージョンを上げずに対応する方法は、解凍すること。
[56] pry(main)> [str].map(&:+@).pack('p*')
=> "\xD8\xC6\b\x04\x00\x00\x00\x00"