Ruby 4.0.0 已发布

naruse 发表于 2025-12-25
翻译: GAO Jun

我们很高兴地宣布 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 之间可以互相隔离:猴子补丁、全局变量、类变量、类/模块定义、加载的原生库和 Ruby 库。

预期使用场景包括:

  • 在 box 中运行带有猴子补丁的测试用例,避免影响其他测试用例
  • 在应用服务器的 Ruby 进程中,并行运行 Web 应用 box,以此进行蓝绿部署
  • 在 Web 应用的 box 中并行运行,在一段时间内,通过响应时间差异来评估依赖更新的效果
  • 用作基础(底层)API,以实现某种“包”(高层)API(尚未设计)。

“Ruby Box” 的详细信息,可以参阅 Ruby::Box。 [Feature #21311] [Misc #21385]

ZJIT

ZJIT 是一个新的即时 (JIT) 编译器,它是作为 YJIT 的下一代产品进行开发的。您需要使用 Rust 1.85.0 或后续版本来构建 Ruby 才能使用, 然后在运行时,指定 --zjit 来启用。

我们之所以创建这个新的 Ruby 编译器,是为了既能够提升性能上限(通过更大的变异单元和 SSA IR), 又能够鼓励更多的外部贡献(通过成为更传统的方法编译器)。更多详情,可以参阅 我们的博客

ZJIT 目前比解释器更快,但还不如 YJIT。我们鼓励您尝试 ZJIT,但目前不建议将其用于生产环境。敬请期待 Ruby 4.1 的 ZJIT。

Ractor 实现

Ractor,即 Ruby 的并行执行机制,获得了多项改进。新类 Ractor::Port 被用来解决消息收发的相关问题(参见 我们的博客)。 另外,Ractor.shareable_proc 让 Ractor 之间共享 Proc 对象更加容易。

性能方面,改进了许多内部数据结构,大大减少了全局锁的争用,从而提升了并行性能。此外,Ractor 现在共享更少的内部数据,这也降低了并行运算时的 CPU 缓存争用。

Ractor 最初在 Ruby 3.0 作为实验性功能被引入。我们计划明年取消其 “实验性” 状态。

语言变化

  • *nil 不再调用 nil.to_a,就像 **nil 不再调用 nil.to_hash。 [Feature #21047]

  • 行首的逻辑二元运算符 (||&&andor) 与行首的 . 一样,延续上一行。下面两段代码是等价的:

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

    [Feature #20925]

核心类更新

注意:我们只列出了重要的核心类更新。

  • Array

    • 新增 Array#rfind,提供了等价于 array.reverse_each.find 的高效方法 [Feature #21678]
    • 新增 Array#find,提供了比 Enumerable#find 更高效的覆盖方法 [Feature #21678]
  • Binding

    • Binding#local_variables 不再包含数字引用参数(如 _1, _2)。 同样, Binding#local_variable_getBinding#local_variable_set 也会拒绝处理数字引用参数。 [Bug #21049]

    • 新增 Binding#implicit_parametersBinding#implicit_parameter_getBinding#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]

  • 错误高亮显示

    • 当抛出了 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

    • 引入和 Kernel#raise 类似的参数支持 Fiber#raise(cause:)。 [Feature #21360]
  • Fiber::Scheduler

    • 引入 Fiber::Scheduler#fiber_interrupt,允许通过指定异常来终端纤程。 典型的用例是当 IO 操作关闭时,中断等待此 IO 的纤程。 [Feature #21166]

    • 引入 Fiber::Scheduler#yield,允许纤程调度器在禁用信号异常时继续处理。 [Bug #21633]

    • 再次引入 Fiber::Scheduler#io_close 钩子来处理异步 IO#close.

    • Fiber::Scheduler#io_write 在刷新 IO 写缓存时调用。 [Bug #21789]

  • File

    • Linux 中,File::Stat#birthtime 在操作系统和文件系统支持的情况下,会通过 statx 系统调用实现。 [Feature #21205]
  • IO

    • IO.select 允许使用 Float::INFINITY 作为超时参数。 [Feature #20610]

    • 声明废弃行为:IO 类以传入 | 起始的字符串来创建进程的功能被移除。 [Feature #19630]

  • Kernel

    • Kernel#inspect 现在会检查是否存在 #instance_variables_to_inspect 方法, 这个方法可以用来控制在 #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 以传入 | 起始的字符串来创建进程的功能被移除。 [Feature #19630]

  • Math

  • Pathname

  • 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 (或 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 现在只接受若干个 Ractor 和 Port 作为参数。 如果参数是若干个 Ractor,方法会在最早结束的 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_set 现在会先检查大小,来避免无上限 range 的问题。 [Bug #21654]

    • Range#overlap? 现在可以正确处理无限(无界) range。 [Bug #21185]

    • Range#max 在无下限整数 range 中的行为已修复。 [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}>)。 [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

    • 引入和 Kernel#raise 类似的参数支持 Thread#raise(cause:)。 [Feature #21360]

标准库更新

我们只列出了重要的标准库更新。

其他变更列在下方。如果 gem 有自己的 GitHub 发布页面,我们还列出了自 Ruby 3.4.0 以来的发布历史。

以下默认 gems 提升为绑定 gem。

新增以下默认 gem。

  • win32-registry 0.1.2

更新以下默认 gem。

更新以下绑定 gem。

RubyGems 和 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_outgoging

    [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 中,”wrong number of arguments” 现在会包含被调用方法的类或模块名。 (如:Foo#bar 替代原来的 bar)。 [Bug #21698]

  • 错误栈现在不再展示 internal 帧。这些方法现在看起来像是在 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 库从默认 gems 中移除。现在 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

    • 此前,在发送带有 body 的请求(如:POSTPUT)时,如果没有明确制定, 那么 HTTP 头 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_EXTERNALIO 实例,然后使用 rb_io_close(io) 进行关闭(此操作也会中断并等待该 IO 上所有的待处理操作)。 直接关闭文件描述符不会中断待处理操作,可能会导致未定义的行为。换言之,如果两个 IO 对象共享相同的文件描述符, 则关闭一个不会影响另一个。 [Feature #18455]
  • GVL

    • 现在,无论是否使用 GVL,rb_thread_call_with_gvl 都可以正常工作。 这使得 gem 可以避免检查 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 堆现在独立增长。当只有某些池包含长期活跃对象时,将减少内存使用量。
  • GC 清除包含大对象的页面的速度更快。
  • “泛型 ivar” 对象(String,Array,TypedData等)现在使用新内部 “fields” 对象来加速实例变量的访问。
  • 在初次使用前,GC 会避免维护内部的 id2ref 表,提升 object_id 的分配和 GC 清除速度。
  • 类和模块对象的 object_idhash 方法更快。
  • 较大的大整数可以通过可变宽度分配保持嵌入状态。
  • RandomEnumerator::ProductEnumerator::ChainAddrinfoStringScanner 以及一些内部对象现在处于写屏障保护中,从而减少 GC 开销。

Ractor

为了让 Ractor 更加稳定、高效、易用,我们投入了大量的工作。 这些改进让 Ractor 的实现逐渐完整,并即将离开实验性状态。

  • 性能提升
    • 冻结字符串和符号表在内部使用了无锁哈希集合 [Feature #21268]
    • 方法缓存查找在多数场景下可以避免锁定
    • 类(以及一般实例变量)实例变量访问速度更快,并且可以避免锁定
    • 通过为每个 ractor 使用计数器,降低对象内存分配时的 CPU 缓存争用
    • 通过使用线程本地计数器,减少 xmallloc/xfree 中的 CPU 缓存争用
    • 在多数场景下 object_id 能避免锁定
  • 错误修复和稳定性提升
    • 修正了同时使用 Ractors 和进程时的可能死锁情况
    • 修正了 Ractor 中 require 和自动载入的问题
    • 修正了 Ractors 间的 编码/转码 问题
    • 修复了 GC 操作和方法失效中的竞争条件
    • 修正了在启动 Ractor 后进行进程分叉的问题
    • Ractor 中的 GC 分配计数现在是准确的
    • 修正了 GC 后,TracePoints 不能正常工作的问题 [Bug #19112]

JIT

  • ZJIT
    • 引入了 实验性的基于方法的 JIT 编译器. ZJIT 可以通过两种方法启用:使用 --zjit 参数,或者调用 RubyVM::ZJIT.enable。 构建 Ruby 时,如要包含 ZJIT 支持,需要使用 Rust 1.85.0 或后续版本来构建。
    • 从 Ruby 4.0.0 起,ZJIT 比解释器更快,但还不及 YJIT。 我们鼓励尝试 ZJIT,但目前不建议将其用于生产环境。
    • 我们的目标是让 ZJIT 比 YJIT 更快,并在 Ruby 4.1 时可用于生产环境。
  • YJIT
    • RubyVM::YJIT.runtime_stats
      • ratio_in_yjit 在标准构建中不可用。 可以在 configure 中设置 --enable-yjit=stats 并在调用时通过 --yjit-stats 来启用。
      • 在默认统计中新增 invalidate_everything,当 TracePoint 失效代码时,此统计信息会递增。
    • RubyVM::YJIT.enable 新增选项 mem_size:call_threshold:
  • RJIT
    • 移除 --rjit。我们将把第三方 JIT API 实现迁移到 ruby/rjit 代码库。

更多详情,可参见 新闻提交日志

自 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

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

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

    文件大小: 29253204
    SHA1: 0b69f89d1d140157251c0d3a6032f6c45cdf81e8
    SHA256: 70cb1bf89279b86ab9a975d504607c051fc05ee03e311d550a5541b65e373455
    SHA512: a72e076ef618c0aeb9d20cf22e6fb12fda36809c0064ef0f98153b95a0bac257ef606342444a38f992c4594bf376a4d264686cf597463aa6f111220798784302
    

Ruby 是什么

Ruby 最初由 Matz (松本行弘,Yukihiro Matsumoto) 于 1993 年开发, 现在以开源软件的形式开发。它可以在多个平台上运行,并在全球得到广泛使用,尤其是 Web 开发领域。

最新消息

全新的 Ruby 文档界面

继 重新设计 ruby-lang.org之后, 我们还有更多消息来庆祝 Ruby 诞生 30 周年:docs.ruby-lang.org 采用了全新的、RDoc 的 Aliki 默认主题。

Stan Lo 发表于 2025-12-23

重新设计我们的网站标识

我们激动地宣布网站已经全面改版。此次更新的设计方案由 Taeko Akatsuka (赤塚妙子) 创作。

Hiroshi SHIBATA 发表于 2025-12-22

更多新闻...