🦁

[Feature #20669] Marshal.load 用の例外を追加する提案

2024/08/09に公開

[Feature #20669] Add Marshal::MarshalError class to differentiate ArgumentErrors

  • marshal はいろんなデータをファイル(文字列)に変換し、そのデータを復元するためのライブラリ
# データを文字列に変換
data = Marshal.dump({ id: 1, name: "homu", age: 14 })
pp data
# => "\x04\b{\b:\aidi\x06:\tnameI\"\thomu\x06:\x06ET:\bagei\x13"

# そのデータを復元する
pp Marshal.load(data)
# => {:id=>1, :name=>"homu", :age=>14}
  • このときに Marshal.load にフォーマットが不正なデータや破損したデータを渡すと次のように TypeErrorArgumentError が発生する
begin
  # 復元できるフォーマットではない場合
  Marshal.load("foobar")
rescue
  pp $!
  # => #<TypeError:"incompatible marshal file format (can't be read)\n\tformat version 4.8 required; 102.111 given">
end

begin
  # データが途中で破損している場合
  Marshal.load(Marshal.dump(Object.new).slice(0, 10))
rescue
  pp $!
  # => #<ArgumentError: marshal data too short>
end

begin
  # 以下のように MyThing に依存するようなデータを復元する場合
  # MyThing = Struct.new(:name, :age)
  # Marshal.dump(MyThing.new("Alice", 20))
  # => "\x04\bS:\fMyThing\a:\tnameI\"\nAlice\x06:\x06ET:\bagei\x19"
  Marshal.load "\x04\bS:\fMyThing\a:\tnameI\"\nAlice\x06:\x06ET:\bagei\x19"
rescue
  pp $!
  # => #<ArgumentError: undefined class/module MyThing>
end
  • TypeErrorArgumentError は通常以下のような場合に発生することを期待するが Marshal.load の場合はそれ以外でも同エラーが発生するのでそれを区別できるようにしたい、というのがこのチケットの内容
    • TypeError : 引数の型が異なる場合
    • ArgumentError : 引数の数が異なる場合
  • これを回避するために Marshal::LoadError を追加する提案が書かれている
    • Marshal の内部でエラーが発生した場合はこの例外を返す想定
  • この提案の問題として互換性の話が上がっています
    • いままで ArgumentError だったものが Marshal::LoadError に変わるので rescue ArgumentError している箇所が非互換になってしまう
  • これに限らず ArgumentError とするのかは難しそうですねえ
GitHubで編集を提案

Discussion