こんにちは、タイミーの @masarakki です。
先日、5月15日から3日間開催された「RubyKaigi2024」に参加しました。
本記事で取り上げるのは、そのRubyKaigi2024の最後のセッションであるmatzのキーノートで、「これが入ったらRuby 4.0」とまで言われた @tagomoris 氏のNamespace機能。
セッション終了後、目の前に本人が座っていたので「責任重大だねwww」と煽りに行こうとしたところ、感極まって帽子を目深に被りなおしている瞬間だったのでそっとしておきました。
というわけで、セッションの内容 は他にいくらでも記事があると思うので、実際に手を動かしてみようと思います。
参考: https://gist.github.com/tagomoris/4392f1091f658294bd4d473d8ff631cb
作業ブランチが Namespace on readにあるのでビルドしてみましょう。
$ git clone https://github.com/tagomoris/ruby $ cd ruby $ git checkout namespace-on-read $ ./autogen.sh $ mkdir build $ cd build $ ../configure --prefix=$HOME/ns-ruby $ make $ make install $ ~/ns-ruby/bin/ruby -v ruby 3.4.0dev (2024-03-28T13:58:33Z namespace-on-read f0649a2577) [x86_64-linux]
どうやらうまくビルドできたようです (rubyのビルド人生で初めてやった)。
かんたんな検証コードを動かしてみましょう。
# foo.rb ------ require './bar' class Foo def self.var=(val) @@var = val end def self.var @@var end end # ------------- # bar.rb ------ class Bar end # ------------- # nstest.rb --- def dump(obj) puts "#{obj}: #{obj.object_id}" end require './foo' ns = Namespace.new ns.require './foo' dump Foo dump Bar dump ns::Foo dump ns::Bar Foo.var = 'abc' ns::Foo.var = 'xyz' puts "#{Foo.var}, #{ns::Foo.var}" # -------------
実行してみましょう。
~/ns-ruby/bin/ruby nstest.rb Foo: 100 Bar: 120 #<Namespace:0x00007ff908cf1cd8>::Foo: 140 #<Namespace:0x00007ff908cf1cd8>::Bar: 160 abc, xyz
Foo
と ns::Foo
が全く独立していることがわかります。
普段遣いのrubyでは動かないことを確認しましょう。
$ ruby -v ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [x86_64-linux] $ ruby nstest.rb nstest.rb:8:in `<main>': uninitialized constant Namespace (NameError) ns = Namespace.new ^^^^^^^^^
確かセッションでは例として Oj.default_options
が 行儀の悪い gem によって書き換えられてしまう事例が挙げられていたので試してみましょう。
# foo.rb ------ require 'oj' Oj.default_options = { symbol_keys: true } class Foo def self.oj Oj.load('{"key":"symbol_or_string"}' end end # ------------- # nstest.rb --- require './foo' ns = Namespace.new ns.require './foo' Oj.default_options = { symbol_keys: false } p Foo.oj p ns::Foo.oj
実行してみましょう。
$ ~/ns-ruby/bin/gem i oj $ ~/ns-ruby/bin/ruby nstest.rb $HOME/ns-ruby/lib/ruby/3.4.0+0/date.rb:51: [BUG] Segmentation fault at 0x0000000000000168 ruby 3.4.0dev (2024-03-28T13:58:33Z namespace-on-read f0649a2577) [x86_64-linux] -- Control frame information ----------------------------------------------- c:0013 p:0039 s:0052 e:000051 CLASS $HOME/ns-ruby/lib/ruby/3.4.0+0/date.rb:51
_人人人人人人人_ > 突然のSEGV <  ̄Y^Y^Y^Y^Y^Y^Y^ ̄
ちなみに date.rb:51
は def coerce(other)
です。 なぜ・・・
そういえばセッションで @tagomoris も祈りながら実行していたな・・・というのを思い出し、何度か実行してみたところ、確率10%くらいで成功しました。
$ ~/ns-ruby/bin/ruby nstest.rb {"key"=>"symbol_or_string"} {:key=>"symbol_or_string"}
他のライブラリでも試してみましょう。
json
標準ライブラリで oj
と同じようにC拡張を持つライブラリです。
特に何も問題なく読み込めました。
csv
'<top (required)>': uninitialized constant Array (NameError)
ネームスペースの中でArrayが見つからないみたいです。
もっと単純なコードで試してみましょう。
# foo.rb ----- puts "#{Array}: #{Array.object_id}" module Mod def foo p :foo end end Array.include(Mod) # ------------ # nstest.rb -- puts "#{Array}: #{Array.object_id}" ns = Namespace.new ns.require './foo' [].foo # ------------
$ ~/ns-ruby/bin/ruby nstest.rb Array: 80 Array: 80 :foo
なにかおかしいですね。
Array
は見つかるものの、予想外にトップレベルの Array
まで汚されてしまっています。
さらに foo.rb
に require 'csv'
を追加すると
$ ~/ns-ruby/bin/ruby nstest.rb $HOME/ns-ruby/lib/ruby/gems/3.4.0+0/specifications/csv-3.2.8.gemspec:4: [BUG] vm_cref_dup: unreachable
_人人人人人人人_ > 突然のSEGV <  ̄Y^Y^Y^Y^Y^Y^Y^ ̄
トップレベルで require ‘csv’
した後に ns.require './foo'
するとuninitialized constant Array (NameError)
のエラーに戻ります
json
と csv
$ ~/ns-ruby/bin/ruby nstest.rb $HOME/ns-ruby/lib/ruby/3.4.0+0/forwardable.rb:230: [BUG] Segmentation fault at 0x00000000000001da -- Control frame information ----------------------------------------------- c:0017 p:---- s:0088 e:000087 CFUNC :proc c:0016 p:0004 s:0084 E:001920 TOP $HOME/ns-ruby/lib/ruby/3.4.0+0/forwardable.rb:230 [FINISH]
_人人人人人人人_ > 突然のSEGV <  ̄Y^Y^Y^Y^Y^Y^Y^ ̄
問題のない json
と問題のある csv
を両方requireするとなぜか別の問題が発生しました。
面白いですね。
(自分のrubyのビルド方法が間違ってる疑惑・・・?)
こういう開発途中の機能を触ってみるのは初めてなので、Rubyがこんなに簡単にぶっ壊れるんだ・・・って新鮮な気持ちです。
いろいろなパターンでSEGVが出たので次回はコードを読んでみたいと思います。
最後に
コロナ禍で人生が一変し、結婚 + 出産 * 2 が続き、2019年以来 実に5年ぶりのRubyKaigi参加になりました。
RubyKaigi、やっぱりドチャクソ楽しいですね・・・特にrubykaraoke・・・ッ!!
1歳と0歳を連れて参加できたのも、ひとえに託児所やイベントに子供を受け入れてくださった各社様のおかげです。この場を借りてお礼申し上げます。
なお、RubyKaigiには「Kaigi Pass」という制度を利用し参加しました。
Kaigi Passとは、世界中で開催されている全ての技術カンファレンスに無制限で参加できるタイミーの制度です。
制度について気になる方はぜひ以下の記事もご覧ください。