🎃

【Ruby 3.4 Advent Calender】【19日目】

2024/12/19に公開

Ruby 3.4 Advent Calender 19日目の記事です。

これはなに

今年 2024年12月25日にリリースされる予定の Ruby 3.4 の新機能や変更点などを1つずつ紹介していく Advent Calender になります。
基本的には NEWS に載っている機能を紹介すると思うんですがここにない機能についても書くかもしれません。
また、記事を書いてる時点ではまだ Ruby 3.4 はリリースされる前なので Ruby 3.4 がリリースされた時点で機能が変わっている 可能性があるかもしれないので注意してください。
記事のまとめは ここを参照 してください。

[Feature #20594] A new String method to append bytes while preserving encoding

Ruby 3.4 からエンコーディングを維持しながら文字列を結合する String#append_as_bytes メソッドが追加されました。

既存の類似メソッドとして #concat, << があります。
これは次のように元の文字列のエンコーディングが変わることがあります。

# 元の文字コードは
base = "somedata".b
pp base.encoding   # => #<Encoding:BINARY (ASCII-8BIT)>

base.concat("あああ")

# 結合後は UTF-8 になる
pp base            # => "somedataあああ"
pp base.encoding   # => #<Encoding:UTF-8>

今回追加された String#append_as_bytes はエンコーディングを保持しながら文字列を結合するメソッドになります。

# 元の文字コードは
base = "somedata".b
pp base.encoding   # => #<Encoding:BINARY (ASCII-8BIT)>

base.append_as_bytes("あああ")

# 元のエンコーディングを保持して文字列を結合する
pp base            # => "somedata\xE3\x81\x82\xE3\x81\x82\xE3\x81\x82"
pp base.encoding   # => #<Encoding:BINARY (ASCII-8BIT)>

背景

提案したチケットでは以下のようなユースケースが提示されていました。
profobufMessagePack などのバイナリプロトコルを利用する場合に以下のようにシリアライズすることがあります。

Post = Struct.new(:title, :body) do
  def serialize(buf)
    buf <<
    255 << title.bytesize << title <<
    255 << body.bytesize << body
  end
end

Post.new("Hello", "World").serialize("somedata".b)
# => "somedata\xFF\x05Hello\xFF\x05World" #<Encoding:ASCII-8BIT>

このときに問題になるのが Encoding::ASCII_8BIT に対して UTF-8 などの文字列を結合しようとした場合にエラーになってしまうので、それを回避したいのがモチベーションになっています。

Post.new("H€llo", "Wôrld").serialize("somedata".b)
# => incompatible character encodings: ASCII-8BIT and UTF-8 (Encoding::CompatibilityError)

元々の提案では String#byteconcat という名前でしたが最終的には String#append_as_bytes という名前で追加されました。

関連

GitHubで編集を提案

Discussion