Ruby 3.2.0 Preview 3 发布

naruse 于 2022 年 11 月 11 日发布

我们高兴地宣布 Ruby 3.2.0-preview3 的发布。Ruby 3.2 增加了许多新特性和性能改进。

基于 WASI 的 WebAssembly 支持

这是 WASI 支持的 WebAssembly 的初步移植。它使得 CRuby 二进制文件可以在 Web 浏览器、无服务器边缘环境和其他 WebAssembly/WASI 嵌入器上可用。目前,此移植通过了不使用 Thread API 的基本和引导测试套件。

背景

WebAssembly (Wasm) 最初是为了在 Web 浏览器中安全快速地运行程序而引入的。但其目标——在各种环境中高效安全地运行程序——长期以来不仅被 Web 需要,也被通用应用程序所需要。

WASI (WebAssembly 系统接口) 就是为此类用例而设计的。虽然这类应用程序需要与操作系统通信,但 WebAssembly 运行在一个没有系统接口的虚拟机上。WASI 对其进行了标准化。

Ruby 中的 WebAssembly/WASI 支持旨在利用这些项目。它使 Ruby 开发者能够编写运行在这些承诺的平台上的应用程序。

用例

此支持鼓励开发者在 WebAssembly 环境中利用 CRuby。它的一个用例是 TryRuby playground 的 CRuby 支持。现在您可以在浏览器中尝试原始 CRuby。

技术要点

今天的 WASI 和 WebAssembly 本身在实现 Fiber、异常和 GC 方面存在一些缺失的功能,因为它们仍在发展中,也出于安全原因。因此,CRuby 通过使用 Asyncify 来弥补这一差距,Asyncify 是一种用于控制用户空间执行的二进制转换技术。

此外,我们在 WASI 之上构建了 一个 VFS,以便我们可以轻松地将 Ruby 应用打包成单个 .wasm 文件。这使得 Ruby 应用的分发更加容易。

Regexp 针对 ReDoS 的改进

众所周知,Regexp 匹配可能需要意想不到的长的时间。如果您的代码尝试将一个可能效率低下的 Regexp 与不可信的输入进行匹配,攻击者可能会利用它进行高效的拒绝服务攻击(所谓的正则表达式 DoS,或 ReDoS)。

我们引入了两项改进,可以显著缓解 ReDoS。

改进的 Regexp 匹配算法

自 Ruby 3.2 起,通过使用记忆化技术,Regexp 的匹配算法得到了极大的改进。

# This matching takes 10 sec. in Ruby 3.1, and does 0.003 sec. in Ruby 3.2

/^a*b?a*$/ =~ "a" * 50000 + "x"

改进的匹配算法使得大多数 Regexp 匹配(在我们实验中约 90%)可以在线性时间内完成。

(对于预览用户:此优化可能会为每次匹配消耗与输入长度成比例的内存。我们预计不会出现实际问题,因为此内存分配通常会延迟,并且正常的 Regexp 匹配消耗的内存最多是输入长度的 10 倍。如果您在实际应用程序中匹配 Regexps 时遇到内存不足的问题,请报告。)

原始提案是 https://bugs.ruby-lang.org/issues/19104

Regexp 超时

上述优化不能应用于某些类型的正则表达式,例如包含高级特性(如反向引用或前瞻/后顾),或具有巨大的固定重复次数。作为一种回退措施,还引入了 Regexp 匹配的超时功能。

Regexp.timeout = 1.0

/^a*b?a*()\1$/ =~ "a" * 50000 + "x"
#=> Regexp::TimeoutError is raised in one second

请注意,Regexp.timeout 是全局配置。如果您想为一些特殊的 Regexps 使用不同的超时设置,您可能想在 Regexp.new 中使用 timeout 关键字。

Regexp.timeout = 1.0

# This regexp has no timeout
long_time_re = Regexp.new('^a*b?a*()\1$', timeout: Float::INFINITY)

long_time_re =~ "a" * 50000 + "x" # never interrupted

原始提案是 https://bugs.ruby-lang.org/issues/17837

其他值得注意的新特性

不再捆绑第三方源

  • 我们不再捆绑第三方源,例如 libyaml, libffi

    • libyaml 源码已从 psych 中移除。您可能需要在 Ubuntu/Debian 平台上安装 libyaml-dev。包名在不同平台下有所不同。

    • 打包的 libffi 源码也已从 fiddle 中移除。

语言

  • 匿名 rest 参数和关键字 rest 参数现在可以作为参数传递,而不仅仅用于方法参数。[Feature #18351]

      def foo(*)
        bar(*)
      end
      def baz(**)
        quux(**)
      end
    
  • 接受单个位置参数和关键字的 proc 将不再自动展开。[Bug #18633]

    proc{|a, **k| a}.call([1, 2])
    # Ruby 3.1 and before
    # => 1
    # Ruby 3.2 and after
    # => [1, 2]
    
  • 对显式对象设置的常量的赋值求值顺序已与单属性赋值的求值顺序一致。使用此代码:

      foo::BAR = baz
    

    现在 foo 会在 baz 之前调用。同样,对于常量的多个赋值,使用从左到右的求值顺序。使用此代码:

        foo1::BAR1, foo2::BAR2 = baz1, baz2
    

    现在使用以下求值顺序:

    1. foo1
    2. foo2
    3. baz1
    4. baz2

    [Bug #15928]

  • Find 模式不再是实验性的。[Feature #18585]

  • 接受 rest 参数(如 *args)并且希望通过 foo(*args) 委托关键字参数的方法,现在必须使用 ruby2_keywords 标记(如果尚未标记)。换句话说,所有希望通过 *args 委托关键字参数的方法现在都必须使用 ruby2_keywords 标记,没有例外。这将更容易在库可以要求 Ruby 3+ 时过渡到其他委托方式。之前,如果接收方法接受 *argsruby2_keywords 标志会被保留,但这是一个 bug 和不一致。查找可能缺失的 ruby2_keywords 的一个好技巧是运行测试套件,找到必须接收关键字参数的最后一个方法,在那里使用 puts nil, caller, nil,并检查调用链上每个必须委托关键字的方法/块是否正确标记为 ruby2_keywords。[Bug #18625] [Bug #16466]

      def target(**kw)
      end
    
      # Accidentally worked without ruby2_keywords in Ruby 2.7-3.1, ruby2_keywords
      # needed in 3.2+. Just like (*args, **kwargs) or (...) would be needed on
      # both #foo and #bar when migrating away from ruby2_keywords.
      ruby2_keywords def bar(*args)
        target(*args)
      end
    
      ruby2_keywords def foo(*args)
        bar(*args)
      end
    
      foo(k: 1)
    

性能改进

YJIT

  • 在 UNIX 平台上支持 arm64 / aarch64。
  • 构建 YJIT 需要 Rust 1.58.1+。[Feature #18481]

自 3.1 以来的其他值得注意的更改

  • Hash
    • 如果哈希为空,Hash#shift 现在始终返回 nil,而不是返回默认值或调用默认 proc。 [Bug #16908]
  • MatchData
  • Module
  • Proc
  • Refinement
  • RubyVM::AbstractSyntaxTree
    • parseparse_fileof 添加了 error_tolerant 选项。[Feature #19013]
  • Set
    • Set 现在作为内置类可用,无需 require "set"。[Feature #16989] 它目前通过 Set 常量或调用 Enumerable#to_set 进行自动加载。
  • String
    • 添加了 String#byteindex 和 String#byterindex。 [Feature #13110]
    • Unicode 更新至 14.0.0 版本,Emoji 更新至 14.0 版本。[Feature #18037] (也适用于 Regexp)
    • 添加了 String#bytesplice。 [Feature #18598]
  • Struct
    • Struct 类也可以使用关键字参数进行初始化,而无需在 Struct.new 上使用 keyword_init: true。[Feature #16806]

兼容性问题

注意:不包括功能错误修复。

已删除的常量

已删除以下已弃用的常量。

已删除的方法

以下已弃用的方法已被移除。

标准库兼容性问题

  • Psych 不再打包 libyaml 源码。用户需要通过包管理系统自行安装 libyaml 库。[Feature #18571]

C API 更新

更新的 C API

已更新以下 API。

  • PRNG 更新 rb_random_interface_t 已更新并版本化。使用此接口并为旧版本构建的扩展库,还需要定义 init_int32 函数。

已删除的 C API

已删除以下已弃用的 API。

  • rb_cData 变量。
  • “taintedness” 和 “trustedness” 函数。[Feature #16131]

标准库更新

  • SyntaxSuggest

    • 之前称为 dead_endsyntax_suggest 功能已集成到 Ruby 中。[Feature #18159]
  • ErrorHighlight

    • 现在它指向 TypeError 和 ArgumentError 的参数。
test.rb:2:in `+': nil can't be coerced into Integer (TypeError)

sum = ary[0] + ary[1]
               ^^^^^^
  • 更新了以下默认 gem。
    • RubyGems 3.4.0.dev
    • bigdecimal 3.1.2
    • bundler 2.4.0.dev
    • cgi 0.3.2
    • date 3.2.3
    • error_highlight 0.4.0
    • etc 1.4.0
    • io-console 0.5.11
    • io-nonblock 0.1.1
    • io-wait 0.3.0.pre
    • ipaddr 1.2.4
    • json 2.6.2
    • logger 1.5.1
    • net-http 0.2.2
    • net-protocol 0.1.3
    • ostruct 0.5.5
    • psych 5.0.0.dev
    • reline 0.3.1
    • securerandom 0.2.0
    • set 1.0.3
    • stringio 3.0.3
    • syntax_suggest 0.0.1
    • timeout 0.3.0
  • 更新了以下捆绑 gem。
    • minitest 5.16.3
    • net-imap 0.2.3
    • rbs 2.6.0
    • typeprof 0.21.3
    • debug 1.6.2
  • 以下默认 gems 现在是打包 gems。

有关更多详细信息,请参阅 NEWScommit 记录

有了这些更改,自 Ruby 3.1.0 以来,已更改 2719 个文件,增加了 191269 行,删除了 120315 行

下载

  • https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.0-preview3.tar.gz

    SIZE: 20086542
    SHA1: dafca8116d36ceaa32482ab38359768de8c3ae5e
    SHA256: c041d1488e62730d3a10dbe7cf7a3b3e4268dc867ec20ec991e7d16146640487
    SHA512: 860634d95e4b9c48f18d38146dfbdc3c389666d45454248a4ccdfc3a5d3cd0c71c73533aabf359558117de9add1472af228d8eaec989c9336b1a3a6f03f1ae88
    
  • https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.0-preview3.tar.xz

    SIZE: 14799804
    SHA1: c94e2add05502cb5c39afffc995b7c8f000f7df0
    SHA256: d3f5619de544240d92a5d03aa289e71bd1103379622c523a0e80ed029a74b3bb
    SHA512: c1864e2e07c3711eaa17d0f85dfbcc6e0682b077782bb1c155315af45139ae66dc4567c73682d326975b0f472111eb0a70f949811cb54bed0b3a816ed6ac34df
    
  • https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.0-preview3.zip

    SIZE: 24426893
    SHA1: 346c051c4be7ab8d0b551fd2ff8169785697db62
    SHA256: cf49aa70e7ebd8abebffd5e49cd3bd92e5b9f3782d587cc7ed88c98dd5f17069
    SHA512: 4f22b5ea91be17ef5f68cf0acb1e3a226dcc549ad71cc9b40e623220087c4065ca9bea942710f668e5c94ca0323da8d2ccd565f95a9085c1a0e38e9c0543b22f
    

什么是 Ruby

Ruby 最初由 Matz (Yukihiro Matsumoto) 于 1993 年开发,现在作为开源项目进行开发。它运行在多个平台上,并在世界各地广泛使用,尤其是在 Web 开发领域。

近期新闻

Ruby 4.0.0 发布

我们很高兴地宣布 Ruby 4.0.0 的发布。Ruby 4.0 引入了“Ruby Box”和“ZJIT”,并增加了许多改进。

naruse 发布于 2025 年 12 月 25 日

Ruby 文档的全新外观

继 ruby-lang.org 重新设计之后,我们还有更多好消息来庆祝 Ruby 成立 30 周年:docs.ruby-lang.org 采用了 Aliki——RDoc 的新默认主题,焕然一新。

Stan Lo 发布于 2025 年 12 月 23 日

重新设计我们的网站标识

我们很高兴地宣布对我们的网站进行全面重新设计。此次更新的设计由 Akatsuka Taeko 创作。

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 日

更多新闻...