Ruby 4.0.0 發布

naruse 發表於 2025-12-25
翻譯: Bear Su

我們很高興宣布 Ruby 4.0.0 發布了。 Ruby 4.0 導入了 Ruby::Box 和 “ZJIT”,以及許多改進功能。

Ruby Box

Ruby Box 是一項用來提供定義區隔的(實驗性質)新功能。 可以透過設定環境變數 RUBY_BOX=1 啟用 Ruby Box。類別是 Ruby::Box

在 Ruby Box 中載入的定義是互相隔離的。 Ruby Box 可以將從其他 boxes 載入的 monkey patches、全域/類別變數、類別/模組定義、和載入的原生/Ruby 函式庫做隔離。

預期的使用場景有:

  • Run test cases in box to protect other tests when the test case uses monkey patches to override something
  • 當測試案例使用 monkey patches 覆蓋時,在 box 環境中執行測試案例可以保護其他測試。
  • 在 Ruby 進程中平行執行 Web 應用伺服器 boxes,以在應用程式伺服器上執行藍綠部署。
  • 執行 Web 應用伺服器 boxes 來用 Ruby 程式碼檢查回應差異,評估特定時間段內的依賴更新。
  • 作為基礎(底層)API,以實現某種「套件」(高層)API(尚未設計)。

參見 Ruby::Box 以了解更多關於「Ruby Box」的細節。 [Feature #21311] [Misc #21385]

ZJIT

ZJIT 是一個新型的即時 (JIT) 編譯器,它是作為 YJIT 的下一代產品而開發的。 要建置支援 ZJIT 的 Ruby,您需要 Rust 1.85.0 或更高版本,指定 --zjit 參數啟用 ZJIT。

我們之所以為 Ruby 建立一個新的編譯器,是因為我們既想提高效能上限(更大的編譯單元大小和 SSA IR),並鼓勵更多外部貢獻(透過成為一個更傳統的編譯器)。 參見 我們的部落格文章 來了解更多。

ZJIT 比直譯器快,但還不如 YJIT 快。 我們鼓勵您嘗試使用 ZJIT,但目前最好不要部署到生產環境中。 敬請期待 Ruby 4.1 的 ZJIT 版本。

Ractor 改進

Ruby 的平行執行機制 Ractor 已經得到了多項改進。 導入了一個新的類別 Ractor::Port,用於解決與訊息發送和接收相關的問題。 (參見 我們的部落格文章)。 此外,Ractor.shareable proc 讓在 Reactor 之間共用 Proc 物件變得更加容易。

在效能方面,許多內部資料結構都得到了改進,顯著減少了對全域鎖定的競爭,從而提高了平行執行的效率。 此外,Ractor 共用的內部資料也減少了,因此在平行執行時,CPU 快取競爭也相應降低。

Ractor 最初在 Ruby 3.0 中作為一項實驗性功能導入。我們計劃明年取消其「實驗性」狀態。

語法變更

  • *nil 不再呼叫 nil.to_a,就跟 **nil 不呼叫 nil.to_hash 一樣。[Feature #21047]

  • 位於行首的邏輯二元運算子(||&&andor)會延續前一行的內容,如同 fluent dot。 以下兩段程式碼是相同的效果:

      if condition1
         && condition2
        ...
      end
    

    之前:

      if condition1 && condition2
        ...
      end
    
      if condition1 &&
         condition2
        ...
      end
    

    [Feature #20925]

核心類別更新

注意:我們只列出特別的類別更新。

  • Array

    • 新增 Array#rfind 提供比 array.reverse_each.find 更有效率的替代方法。 [Feature #21678]
    • 新增 Array#find 提供比 Enumerable#find 更有效率的 Override 方法。 [Feature #21678]
  • Binding

    • Binding#local_variables 不再包含編號參數。 同時,Binding#local_variable_getBinding#local_variable_setBinding#local_variable_defined? 也不再處理編號參數。 [Bug #21049]

    • 新增Binding#implicit_parametersBinding#implicit_parameter_get、和 Binding#implicit_parameter_defined? 存取編號參數和 it 參數。 [Bug #21049]

  • Enumerator

    • Enumerator.produce 現在接受一個可選的 size 關鍵字參數,用於指定列舉的大小。 參數可以是個整數、Float::INFINITY、可呼叫的對象(如 lambda)、或是 nil 表示未知大小。 未指定時,大小預設為 Float::INFINITY

        # 無限列舉
        enum = Enumerator.produce(1, size: Float::INFINITY, &:succ)
        enum.size  # => Float::INFINITY
      
        # 指定了已知/可計算大小的有限列舉
        abs_dir = File.expand_path("./baz") # => "/foo/bar/baz"
        traverser = Enumerator.produce(abs_dir, size: -> { abs_dir.count("/") + 1 }) {
          raise StopIteration if it == "/"
          File.dirname(it)
        }
        traverser.size  # => 4
      

      [Feature #21701]

  • ErrorHighlight

    • 當拋出 ArgumentError 時,現在會顯示方法呼叫(呼叫者)和方法定義(被呼叫者)的程式碼片段。 [Feature #21543]

      test.rb:1:in 'Object#add': wrong number of arguments (given 1, expected 2) (ArgumentError)
      
          caller: test.rb:3
          | add(1)
            ^^^
          callee: test.rb:1
          | def add(x, y) = x + y
                ^^^
              from test.rb:3:in '<main>'
      
  • Fiber

    • 導入了 Fiber#raise(cause:) 參數支援,與 Kernel#raise 的用法類似。 [Feature #21360]
  • Fiber::Scheduler

    • 導入了 Fiber::Scheduler#fiber_interrupt,可用於拋出特定的例外來中斷 fiber。 最初的使用案例是當 IO 操作關閉時,用來中斷正在等待該阻塞 IO 操作的 fiber。 [Feature #21166]

    • 導入 Fiber::Scheduler#yield,允許 Fiber 調度器在訊號異常被停用時繼續處理。 [Bug #21633]

    • 重新導入 Fiber::Scheduler#io_close hook,用於非同步 IO#close

    • 當刷新 IO 寫入緩衝區時呼叫 Fiber::Scheduler#io_write。 [Bug #21789]

  • File

    • File::Stat#birthtime 現在可在核心與系統支援 statx 系統呼叫 的 Linux 上使用。 [Feature #21205]
  • IO

    • IO.select 允許 Float::INFINITY 作為逾時參數。 [Feature #20610]

    • 移除被棄用的行為:透過 IO 並在開頭使用 | 符號來建立行程 [Feature #19630]

  • Kernel

    • Kernel#inspect 現在會檢查是否存在 #instance_variables_to_inspect 方法,這讓開發者可以控制哪些實體變數(instance variables)可以顯示在 #inspect 的字串中:

        class DatabaseConfig
          def initialize(host, user, password)
            @host = host
            @user = user
            @password = password
          end
      
          private def instance_variables_to_inspect = [:@host, :@user]
        end
      
        conf = DatabaseConfig.new("localhost", "root", "hunter2")
        conf.inspect #=> #<DatabaseConfig:0x0000000104def350 @host="localhost", @user="root">
      

      [Feature #21219]

    • 移除被棄用的行為:透過 Kernel#open 並在開頭使用 | 符號來建立 process。 [Feature #19630]

  • Math

  • Pathname

    • Pathname 已從預設 gem 升級為 Ruby 的核心類別。 [Feature #17473]
  • Proc

    • Proc#parameters 現在會將匿名可選參數顯示為 [:opt] 而非 [:opt, nil],使得其輸出與匿名參數為必要時的格式保持一致。 [Bug #20974]
  • Ractor

    • 新增了 Ractor::Port 類別,為 Ractor 之間的通訊提供了一種新的同步機制。 [Feature #21262]

        port1 = Ractor::Port.new
        port2 = Ractor::Port.new
        Ractor.new port1, port2 do |port1, port2|
          port1 << 1
          port2 << 11
          port1 << 2
          port2 << 12
        end
        2.times{ p port1.receive } #=> 1, 2
        2.times{ p port2.receive } #=> 11, 12
      

      Ractor::Port 提供以下方法:

      • Ractor::Port#receive
      • Ractor::Port#send (or Ractor::Port#<<)
      • Ractor::Port#close
      • Ractor::Port#closed?

      最後移除了 Ractor.yieldRactor#take

    • 新增了 Ractor#joinRactor#value 用於等待 Ractor 終止。這些方法與 Thread#joinThread#value 類似。

    • 新增了 Ractor#monitorRactor#unmonitor,作為內部實作 Ractor#join 時所使用的低階介面。

    • Ractor.select 現在僅接受 Ractors 與 Ports。若傳入的是 Ractors,它會在該 Ractor 終止時回傳。

    • 新增了 Ractor#default_port。每個 Ractor 都有一個預設埠,供 Ractor.sendRactor.receive 使用。

    • 移除了 Ractor#close_incomingRactor#close_outgoing

    • 導入了 Ractor.shareable_procRactor.shareable_lambda 以建立共享的 Proc 或 lambda。 [Feature #21550], [Feature #21557]

  • Range

    • Range#to_setEnumerator#to_set 現在會進行大小檢查,以避免發生無窮範圍的問題。 [Bug #21654]

    • Range#overlap? 現在能正確處理無限(無邊界)範圍。 [Bug #21185]

    • 已修正 Range#max 對於無開頭整數範圍的行為。 [Bug #21174] [Bug #21175]。

  • Ruby

    • 定義了一個新的頂層模組 Ruby,其中包含與 Ruby 相關的常數。此模組在 Ruby 3.4 中已被預留,現在則是正式定義。 [Feature #20884]
  • Ruby::Box

  • Set

    • Set 現在是核心類別,而非自動載入的標準函式庫類別。 [Feature #21216]

    • Set#inspect 現在回傳適合用於 eval 的字串,並使用 Set[] 語法(例如:使用 Set[1, 2, 3] 而非 #<Set: {1, 2, 3}>)。這讓它與 Array 和 Hash 等其他核心集合類別保持一致。 [Feature #21389]

    • Set#to_setEnumerable#to_set 傳遞參數的行為現在已被棄用。 [Feature #21390]

  • Socket

    • Socket.tcpTCPSocket.new 現在接受 open_timeout 關鍵字參數,可用於指定建立初始連線時的逾時時間。 [Feature #21347]

    • 當使用者在 TCPSocket.new 中指定逾時時間時,在之前依據情況可能會引發 Errno::ETIMEDOUTIO::TimeoutError。 該行為已改為一致,現在始終會拋出 IO::TimeoutError 錯誤。 (請注意,在 Socket.tcp 中,仍然存在類似情況下拋出 Errno::ETIMEDOUT 異常的可能,在這兩種情況下,Errno::ETIMEDOUT 異常都可能在作業系統層級發生逾時時拋出。 )

  • String

  • Thread

    • 導入了 Thread#raise(cause:) 參數支援,與 Kernel#raise 的用法類似。 [Feature #21360]

標準函式庫更新

我們只列出特別的標準函式庫更新。

若該項目在 GitHub 上有提供發布紀錄,我們也列出了從前一個隨附版本(即 Ruby 3.4.0)以來的發布歷史。

以下預設 gem 變更為隨附 gem:

新增了以下預設 gem。

  • win32-registry 0.1.2

更新了以下預設 gem。

更新了以下隨附 gem。

RubyGems and Bundler

Ruby 4.0 隨附 RubyGems 和 Bundler 4。詳情請參閱以下連結。

支援的平台

  • Windows

    • 停止支援低於 14.0 (_MSC_VER 1900) 的 MSVC 版本。這意味著現在需要使用 Visual Studio 2015 或更新版本。

相容性問題

  • 由於新增了 Ractor::Port,下列方法已從 Ractor 中移除:

    • Ractor.yield
    • Ractor#take
    • Ractor#close_incoming
    • Ractor#close_outgoing

    [Feature #21262]

  • ObjectSpace._id2ref 已被棄用。 [Feature #15408]

  • Process::Status#&Process::Status#>> 已被移除。 它們在 Ruby 3.3. 被棄用。 [Bug #19868]

  • rb_path_check 已被移除。 此函數曾用在已於 Ruby 2.7 移除的 $SAFE 路徑檢查,且此函數先前就已被棄用。 [Feature #20971]

  • 現在對於「參數數量錯誤」的 ArgumentError 的 backtrace 會包含接收者的類別名稱或模組名稱(例如,在 Foo#bar 中,而不是在 bar 中)。 [Bug #21698]

  • Backtraces 不再顯示「內部」幀。這些方法現在看起來像是在 Ruby 原始檔中,與其他 C 語言實作的方法保持一致。 [Bug #20968]

    之前:

    ruby -e '[1].fetch_values(42)'
    <internal:array>:211:in 'Array#fetch': index 42 outside of array bounds: -1...1 (IndexError)
            from <internal:array>:211:in 'block in Array#fetch_values'
            from <internal:array>:211:in 'Array#map!'
            from <internal:array>:211:in 'Array#fetch_values'
            from -e:1:in '<main>'
    

    之後:

    $ ruby -e '[1].fetch_values(42)'
    -e:1:in 'Array#fetch_values': index 42 outside of array bounds: -1...1 (IndexError)
            from -e:1:in '<main>'
    

標準函式庫相容問題

  • CGI 函式庫已從預設 gem 中移除。現在僅針對以下方法提供 cgi/escape

    • CGI.escapeCGI.unescape
    • CGI.escapeHTMLCGI.unescapeHTML
    • CGI.escapeURIComponentCGI.unescapeURIComponent
    • CGI.escapeElementCGI.unescapeElement

    [Feature #21258]

  • 隨著 Set 從標準函式庫移至核心類別,set/sorted_set.rb 已被移除,且 SortedSet 不再是自動載入的常數。 若要使用 SortedSet,請安裝 sorted_set gem 並執行 require 'sorted_set'。 [Feature #21287]

  • Net::HTTP

    • 對於帶有請求主體的請求(例如,POSTPUT),已移除如果未明確設定標頭,預設自動將 Content-Type 標頭設為 application/x-www-form-urlencoded 的行為。 如果您的應用程式依賴此自動預設值,則您的要求現在將不帶 Content-Type 標頭發送,這可能會破壞與某些伺服器的相容性。 [GH-net-http #205]

C API 更新

  • IO

    • rb_thread_fd_close 已被棄用,現在沒有作用。如果您需要從 C 擴充功能將檔案描述符提供給 Ruby 程式碼,請使用 RUBY_IO_MODE_EXTERNAL 建立一個 IO 實體,並使用 rb_io_close(io) 來關閉它(這同時會中斷並等待該 IO 實體上所有進行中的操作)。 直接關閉檔案描述符不會中斷進行中的操作,且可能導致未定義行為。 換句話說,如果兩個 IO 物件共享同一個檔案描述符,關閉其中一個並不會影響另一個。 [Feature #18455]
  • GVL

    • rb_thread_call_with_gvl 現在不論是否持有 GVL 均可運作。這讓 gems 可以不必再檢查 ruby_thread_has_gvl_p。 在使用 GVL 時仍請保持謹慎。 [Feature #20750]
  • Set

    • 已新增 Set 的 C API。支援以下方法: [Feature #21459]

      • rb_set_foreach
      • rb_set_new
      • rb_set_new_capa
      • rb_set_lookup
      • rb_set_add
      • rb_set_clear
      • rb_set_delete
      • rb_set_size

實作改進

  • Class#new(例如 Object.new)在所有情況下都更快,尤其是在傳遞關鍵字參數時。此功能也已整合到 YJIT 和 ZJIT 中。 [Feature #21254]
  • 不同大小集區的 GC heaps 現在獨立增長,當只有部分集區包含長期存活的物件時,可以減少記憶體使用量。
  • 在包含大量物件的分頁上 GC 掃描速度更快。
  • 「Generic ivar」物件(String、Array、TypedData 等)現在使用新的內部「fields」對象,以加快實例變數的存取速度。
  • GC 避免在首次使用之前維護內部的 id2ref 表,從而加快了 object_id 的分配和 GC 掃描的速度。
  • object_idhash 在類別與模組物件上更快。
  • 較大的大數整數可以使用可變寬度分配來保持嵌入狀態。
  • RandomEnumerator::ProductEnumerator::ChainAddrinfo, StringScanner、和一些內部物件現在也受到寫入屏障保護,這降低了 GC 的開銷。

Ractor

投入了大量心力來提升 Ractor 的穩定性、效能與易用性。這些改進使得 Ractor 的實作更接近脫離實驗性質的狀態。

  • 效能提升
    • 凍結字串與符號表在內部使用無鎖雜湊集合。 [Feature #21268]
    • 方法快取查詢在多數情況下避免了鎖定。
    • 類別(以及 eneric ivar)的實體變數存取速度更快,並避免了鎖定。
    • 使用 per-ractor counter 在分配物件時避免 CPU 快取衝突。
    • 使用 thread-local counter 在 in xmalloc/xfree 時避免 CPU 快取衝突。
    • object_id 在多數情況下避免了鎖定。
  • 修復錯誤與可靠性
    • 修正了同時使用 Ractor 與 Thread 時可能發生的死鎖。
    • 修正了在 Ractor 中使用 require 與 autoload 的問題。
    • 修正了跨 Ractor 的編碼與轉碼問題。
    • 修正了垃圾回收操作與方法失效時的競爭條件。
    • 修正了啟動 Ractor 後,行程進行複製時的問題。
    • 現在在 Ractors 下垃圾收集器的分配計數是準確的。
    • 修正了垃圾回收後 TracePoints 無法正常工作的問題。 [Bug #19112]

JIT

  • ZJIT
    • 導入 基於實驗性方法的 JIT 編譯器。 如果可用,在執行時使用 --zjit 選項或呼叫 RubyVM::ZJIT.enable 啟用 ZJIT,。 若要啟用 --zjit 支援,請使用 Rust 1.85.0 或更新版本來編譯 Ruby。
    • 在 Ruby 4.0.0,ZJIT 的速度已超越直譯器,但尚未達到 YJIT 的水準。我們鼓勵大家嘗試 ZJIT,但目前建議不要將其部署於正式環境。
    • 我們的目標是在 Ruby 4.1 中讓 ZJIT 的速度超越 YJIT,並達到可供正式環境使用的水準。
  • YJIT
    • RubyVM::YJIT.runtime_stats
      • ratio_in_yjit 不再於預設建置中運作。 請使用 --enable-yjit=statsconfigure 中啟用 --yjit-stats
      • 預設統計新增 invalidate_everything,當所有程式碼被 TracePoint 無效化時遞增。
    • RubyVM::YJIT.enable 新增 mem_size:call_threshold: 選項。
  • RJIT
    • 移除 --rjit。我們將把第三方 JIT API 的實作移至 ruby/rjit 儲存庫。

參見 NEWScommit logs 來了解更多。

自 Ruby 3.4.0 以來,計 3889 檔案變更,230769 行新增(+),297003 行刪減(-)

耶誕快樂、佳節愉快,享受與 Ruby 4.0 一起寫程式的時光!

下載

  • https://cache.ruby-lang.org/pub/ruby/4.0/ruby-4.0.0.tar.gz

    SIZE: 23955109
    SHA1: 754e39e9ad122e1b6deaed860350bac133a35ed3
    SHA256: 2e8389c8c072cb658c93a1372732d9eac84082c88b065750db1e52a5ac630271
    SHA512: 688254e939b197d564e896fb951bc1abf07142f489e91c5ed0b11f68f52d6adb6b1f86616fe03f1f0bb434beeef7e75e158b9c616afb39bb34403b0b78d2ee19
    
  • https://cache.ruby-lang.org/pub/ruby/4.0/ruby-4.0.0.tar.xz

    SIZE: 18008368
    SHA1: 05ec670e86f84325c5353ef2f2888e53b6adc602
    SHA256: a72bacee9de07283ebc19baa4ac243b193129f21aa4e168c7186fb1fe7d07fe1
    SHA512: 2d5b2e566eaf70a5f3ea6ce6afc0611c0415de58a41336ef7a0b855c9a91eda9aa790a5f8b48e40a1eb9d50f8ea0f687216e617f16c8d040a08474f3116518a4
    
  • https://cache.ruby-lang.org/pub/ruby/4.0/ruby-4.0.0.zip

    SIZE: 29253204
    SHA1: 0b69f89d1d140157251c0d3a6032f6c45cdf81e8
    SHA256: 70cb1bf89279b86ab9a975d504607c051fc05ee03e311d550a5541b65e373455
    SHA512: a72e076ef618c0aeb9d20cf22e6fb12fda36809c0064ef0f98153b95a0bac257ef606342444a38f992c4594bf376a4d264686cf597463aa6f111220798784302
    

Ruby 是什麼

Ruby 最初由 Matz(Yukihiro Matsumoto)於 1993 年開發的開源軟體。可以在許多平台上執行。使用者來自世界各地,特別活躍於網路開發領域。

最新消息

Ruby 文件的新樣貌

繼重新設計 ruby-lang.org 之後,我們還有更多消息來慶祝 Ruby 誕生 30 週年: docs.ruby-lang.org 採用了 Aliki—RDoc’s 的全新預設主題。

Stan Lo 發表於 2025-12-23

重新設計我們的網站形象

我們很興奮地宣布網站進行了全面改版。這次更新的設計方案是由 Taeko Akatsuka 負責創作。

Hiroshi SHIBATA 發表於 2025-12-22

Ruby 4.0.0 preview3 發布

我們很高興宣布 Ruby 4.0.0-preview3 發布了。 Ruby 4.0 導入了 Ruby::Box 和 “ZJIT”,以及其他改進功能。

naruse 發表於 2025-12-18

更多新聞...