Вышел Ruby 4.0.0

Опубликовал naruse 25-12-2025
Перевел: ablzh

Мы рады сообщить о выпуске Ruby 4.0.0. Ruby 4.0 представляет «Ruby Box» и «ZJIT», а также добавляет множество улучшений.

Ruby Box

Ruby Box — это новая (экспериментальная) возможность для обеспечения разделения определений. Ruby Box включается, когда задана переменная окружения RUBY_BOX=1. Класс — Ruby::Box.

Определения, загруженные в box (коробку), изолированы внутри неё. Ruby Box может изолировать/разделять monkey patches, изменения глобальных переменных и переменных класса, определения классов/модулей и загруженные нативные/ruby библиотеки от других boxes.

Ожидаемые сценарии использования:

  • Запуск тест-кейсов в box для защиты других тестов, когда тест-кейс использует monkey patches для переопределения чего-либо
  • Параллельный запуск boxes веб-приложения для выполнения blue-green развертывания на сервере приложений в процессе Ruby
  • Параллельный запуск boxes веб-приложения для оценки обновлений зависимостей в течение определенного периода времени путем проверки разницы ответов с использованием кода Ruby
  • Использование в качестве базового (низкоуровневого) API для реализации API типа «пакет» (высокоуровневого) (он пока не спроектирован)

Подробнее о «Ruby Box» смотрите Ruby::Box. [Feature #21311] [Misc #21385]

ZJIT

ZJIT — это новый just-in-time (JIT) компилятор, который разрабатывается как следующее поколение YJIT. Вам нужен Rust 1.85.0 или новее для сборки Ruby с поддержкой ZJIT, и ZJIT включается, когда указан --zjit.

Мы создаем новый компилятор для Ruby, потому что хотим одновременно поднять потолок производительности (больший размер единицы компиляции и SSA IR) и поощрить больший вклад извне (став более традиционным компилятором методов). Смотрите наш пост в блоге для подробностей.

ZJIT быстрее интерпретатора, но пока не так быстр, как YJIT. Мы призываем вас экспериментировать с ZJIT, но, возможно, воздержаться от развертывания его в продакшене на данный момент. Следите за новостями о ZJIT в Ruby 4.1.

Улучшения Ractor

Ractor, механизм параллельного выполнения Ruby, получил несколько улучшений. Был представлен новый класс Ractor::Port для решения проблем, связанных с отправкой и получением сообщений (смотрите наш пост в блоге). Кроме того, Ractor.shareable_proc упрощает совместное использование объектов Proc между Ractor-ами.

Что касается производительности, многие внутренние структуры данных были улучшены для значительного снижения конкуренции за глобальную блокировку, открывая лучшую параллельность. Ractor-ы теперь также разделяют меньше внутренних данных, что приводит к меньшей конкуренции за кэш CPU при параллельном запуске.

Ractor был впервые представлен в Ruby 3.0 как экспериментальная возможность. Мы стремимся снять с него статус «экспериментальный» в следующем году.

Изменения языка

  • *nil больше не вызывает nil.to_a, подобно тому, как **nil не вызывает nil.to_hash. [Feature #21047]

  • Логические бинарные операторы (||, &&, and и or) в начале строки продолжают предыдущую строку, как 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 [Feature #21678]
  • Binding

    • Binding#local_variables больше не включает нумерованные параметры. Также Binding#local_variable_get, Binding#local_variable_set и Binding#local_variable_defined? отказываются обрабатывать нумерованные параметры. [Bug #21049]

    • Binding#implicit_parameters, Binding#implicit_parameter_get и Binding#implicit_parameter_defined? были добавлены для доступа к нумерованным параметрам и параметру “it”. [Bug #21049]

  • Enumerator

    • Enumerator.produce теперь принимает опциональный именованный аргумент size для указания размера энумератора. Это может быть целое число, Float::INFINITY, вызываемый объект (например, лямбда) или nil для обозначения неизвестного размера. Если не указано, размер по умолчанию Float::INFINITY.

        # Infinite enumerator
        enum = Enumerator.produce(1, size: Float::INFINITY, &:succ)
        enum.size  # => Float::INFINITY
      
        # Finite enumerator with known/computable size
        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, теперь отображаются фрагменты кода для вызова метода (caller) и определения метода (callee). [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 для прерывания файбера заданным исключением. Первоначальный сценарий использования — прерывание файбера, который ожидает блокирующей операции ввода-вывода, когда операция ввода-вывода закрывается. [Feature #21166]

    • Представлен Fiber::Scheduler#yield, чтобы позволить планировщику файберов продолжать обработку, когда исключения сигналов отключены. [Bug #21633]

    • Вновь представлен хук Fiber::Scheduler#io_close для асинхронного IO#close.

    • Вызов Fiber::Scheduler#io_write при сбросе буфера записи IO. [Bug #21789]

  • File

    • File::Stat#birthtime теперь доступен в Linux через системный вызов 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

    • Pathname был повышен с стандартного гема до основного класса 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 (или Ractor::Port#<<)
      • Ractor::Port#close
      • Ractor::Port#closed?

      В результате Ractor.yield и Ractor#take были удалены.

    • Ractor#join и Ractor#value были добавлены для ожидания завершения Ractor. Они аналогичны Thread#join и Thread#value.

    • Ractor#monitor и Ractor#unmonitor были добавлены как низкоуровневые интерфейсы, используемые внутри для реализации Ractor#join.

    • Ractor.select теперь принимает только Ractor-ы и Port-ы. Если переданы Ractor-ы, он возвращает, когда Ractor завершается.

    • Был добавлен Ractor#default_port. Каждый Ractor имеет порт по умолчанию, который используется Ractor.send, Ractor.receive.

    • Ractor#close_incoming и Ractor#close_outgoing были удалены.

    • Представлены Ractor.shareable_proc и Ractor.shareable_lambda для создания shareable Proc или lambda. [Feature #21550], [Feature #21557]

  • Range

    • Range#to_set теперь выполняет проверки размера для предотвращения проблем с бесконечными диапазонами. [Bug #21654]

    • Range#overlap? теперь корректно обрабатывает бесконечные (неограниченные) диапазоны. [Bug #21185]

    • Поведение Range#max на целочисленных диапазонах без начала (beginless) было исправлено. [Bug #21174] [Bug #21175]

  • Ruby

    • Был определен новый модуль верхнего уровня Ruby, который содержит константы, связанные с Ruby. Этот модуль был зарезервирован в Ruby 3.4 и теперь официально определен. [Feature #20884]
  • Ruby::Box

    • Новая (экспериментальная) возможность для обеспечения разделения определений. Подробнее о «Ruby Box» смотрите doc/language/box.md. [Feature #21311] [Misc #21385]
  • Set

    • Set теперь основной класс, вместо автозагружаемого класса стандартной библиотеки. [Feature #21216]

    • Set#inspect теперь использует более простое отображение, подобно литеральным массивам. (например, Set[1, 2, 3] вместо #<Set: {1, 2, 3}>). [Feature #21389]

    • Передача аргументов в Set#to_set и Enumerable#to_set теперь устарела. [Feature #21390]

  • Socket

    • Socket.tcp и TCPSocket.new принимают именованный аргумент open_timeout для указания таймаута для начального соединения. [Feature #21347]
    • Когда в TCPSocket.new происходил заданный пользователем таймаут, ранее могло вызываться либо Errno::ETIMEDOUT, либо IO::TimeoutError в зависимости от ситуации. Это поведение было унифицировано, так что теперь последовательно вызывается IO::TimeoutError. (Пожалуйста, обратите внимание, что в Socket.tcp все еще есть случаи, когда Errno::ETIMEDOUT может быть вызван в подобных ситуациях, и что в обоих случаях Errno::ETIMEDOUT может быть вызван, когда таймаут происходит на уровне ОС.)
  • String

    • Обновление Unicode до версии 17.0.0 и Emoji версии 17.0. [Feature #19908][Feature #20724][Feature #21275] (также применяется к Regexp)

    • String#strip, strip!, lstrip, lstrip!, rstrip и rstrip! расширены для принятия аргументов *selectors. [Feature #21552]

  • Thread

    • Представлена поддержка аргумента Thread#raise(cause:) аналогично Kernel#raise. [Feature #21360]

Обновления стандартной библиотеки (Stdlib)

Мы перечисляем только изменения stdlib, которые являются заметными изменениями функциональности.

Другие изменения перечислены в следующих разделах. Мы также перечислили историю релизов с предыдущей поставляемой версии, которой является Ruby 3.4.0, если у нее есть релизы на GitHub.

Следующие поставляемые (bundled) гемы повышены с дефолтных (default) гемов.

Следующий дефолтный гем добавлен.

  • win32-registry 0.1.2

Следующие дефолтные гемы обновлены.

Следующие поставляемые (bundled) гемы обновлены.

RubyGems и Bundler

Ruby 4.0 включает RubyGems и Bundler версии 4. Смотрите следующие ссылки для подробностей.

Поддерживаемые платформы

  • Windows

    • Прекращена поддержка версий MSVC старее 14.0 (_MSC_VER 1900). Это означает, что Visual Studio 2015 или новее теперь требуется.

Проблемы совместимости

  • Следующие методы были удалены из Ractor из-за добавления Ractor::Port:

    • 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 был удален. Эта функция использовалась для проверки пути $SAFE, которая была удалена в Ruby 2.7, и уже была устаревшей. [Feature #20971]

  • Трассировка стека для ArgumentError о “неправильном количестве аргументов” теперь включает имя класса или модуля получателя (например, в 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>'
    

Проблемы совместимости стандартной библиотеки (Stdlib)

  • Библиотека CGI удалена из дефолтных гемов. Теперь мы предоставляем только cgi/escape для следующих методов:

    • CGI.escape и CGI.unescape
    • CGI.escapeHTML и CGI.unescapeHTML
    • CGI.escapeURIComponent и CGI.unescapeURIComponent
    • CGI.escapeElement и CGI.unescapeElement

    [Feature #21258]

  • С переносом Set из stdlib в основной класс, set/sorted_set.rb был удален, и SortedSet больше не является автозагружаемой константой. Пожалуйста, установите гем sorted_set и require 'sorted_set', чтобы использовать SortedSet. [Feature #21287]

  • Net::HTTP

    • Поведение по умолчанию автоматической установки заголовка Content-Type в application/x-www-form-urlencoded для запросов с телом (например, POST, PUT), когда заголовок не был явно установлен, было удалено. Если ваше приложение полагалось на это автоматическое поведение по умолчанию, ваши запросы теперь будут отправляться без заголовка Content-Type, что потенциально нарушает совместимость с некоторыми серверами. [GH-net-http #205]

Обновления C API

  • IO

    • rb_thread_fd_close устарел и теперь является no-op (ничего не делает). Если вам нужно открыть файловые дескрипторы из расширений C для кода Ruby, создайте экземпляр IO, используя RUBY_IO_MODE_EXTERNAL, и используйте rb_io_close(io) для его закрытия (это также прерывает и ожидает все ожидающие операции на экземпляре IO). Прямое закрытие файловых дескрипторов не прерывает ожидающие операции и может привести к неопределенному поведению. Другими словами, если два объекта IO разделяют один и тот же файловый дескриптор, закрытие одного не влияет на другой. [Feature #18455]
  • GVL

    • rb_thread_call_with_gvl теперь работает с или без GVL. Это позволяет гемам избегать проверки ruby_thread_has_gvl_p. Пожалуйста, по-прежнему будьте внимательны с GVL. [Feature #20750]
  • Set

    • C API для Set был добавлен. Следующие методы поддерживаются: [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 разных размеров (pools) теперь растут независимо, уменьшая использование памяти, когда только некоторые пулы содержат долгоживущие объекты
  • GC sweeping (очистка) быстрее на страницах больших объектов
  • Объекты “Generic ivar” (String, Array, TypedData и т.д.) теперь используют новый внутренний объект “fields” для более быстрого доступа к переменным экземпляра
  • GC избегает поддержки внутренней таблицы id2ref до тех пор, пока она не будет впервые использована, делая выделение object_id и очистку GC быстрее
  • object_id и hash быстрее на объектах Class и Module
  • Большие bignum целые числа могут оставаться встроенными (embedded), используя allocation переменной ширины
  • Random, Enumerator::Product, Enumerator::Chain, Addrinfo, StringScanner и некоторые внутренние объекты теперь защищены write-barrier, что уменьшает накладные расходы GC.

Ractor

Много работы было проделано, чтобы сделать Ractor-ы более стабильными, производительными и пригодными к использованию. Эти улучшения приближают реализацию Ractor к выходу из статуса экспериментальной.

  • Улучшения производительности
    • Замороженные строки и таблица символов внутренне используют hash set без блокировок [Feature #21268]
    • Поиски в кэше методов избегают блокировок в большинстве случаев
    • Доступ к переменным экземпляра класса (и generic ivar) быстрее и избегает блокировок
    • Конкуренция за кэш CPU избегается при выделении объектов за счет использования счетчика на каждый ractor
    • Конкуренция за кэш CPU избегается в xmalloc/xfree за счет использования локального для потока счетчика
    • object_id избегает блокировок в большинстве случаев
  • Исправления ошибок и стабильность
    • Исправлены возможные взаимные блокировки (deadlocks) при объединении Ractor-ов и потоков (Threads)
    • Исправлены проблемы с require и autoload в Ractor
    • Исправлены проблемы кодирования/перекодирования между Ractor-ами
    • Исправлены состояния гонки (race conditions) в операциях GC и инвалидации методов
    • Исправлены проблемы с форкингом процессов после запуска Ractor
    • Подсчеты выделения GC теперь точны под Ractor-ами
    • Исправлено: TracePoints не работали после GC [Bug #19112]

JIT

  • ZJIT
    • Представлен экспериментальный JIT компилятор на основе методов. Где доступно, ZJIT может быть включен во время выполнения с опцией --zjit или вызовом RubyVM::ZJIT.enable. При сборке Ruby требуется Rust 1.85.0 или новее для включения поддержки ZJIT.
    • По состоянию на Ruby 4.0.0, ZJIT быстрее интерпретатора, но пока не так быстр, как YJIT. Мы поощряем эксперименты с ZJIT, но советуем воздержаться от развертывания его в продакшене на данный момент.
    • Наша цель — сделать ZJIT быстрее YJIT и готовым к продакшену в Ruby 4.1.
  • YJIT
    • RubyVM::YJIT.runtime_stats
      • ratio_in_yjit больше не работает в сборке по умолчанию. Используйте --enable-yjit=stats при configure, чтобы включить его в --yjit-stats.
      • Добавлен invalidate_everything в статистику по умолчанию, который увеличивается, когда весь код инвалидируется TracePoint-ом.
    • Добавлены опции mem_size: и call_threshold: в RubyVM::YJIT.enable.
  • RJIT
    • --rjit удален. Мы перенесем реализацию стороннего JIT API в репозиторий ruby/rjit.

Смотрите NEWS или логи коммитов для подробностей.

С этими изменениями, 3889 файлов изменено, 230769 вставок(+), 297003 удалений(-) с момента выхода Ruby 3.4.0!

Счастливого Рождества, С Новым Годом, и приятного кодинга с 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 году, и сейчас разрабатывается как Open Source. Он работает на множестве платформ и используется по всему миру, особенно для веб-разработки.

Последние новости

Новый облик документации Ruby

Вслед за редизайном ruby-lang.org, у нас есть еще новости в честь 30-летия Ruby: docs.ruby-lang.org получил полностью новый облик с Aliki — новым стандартным шаблоном RDoc....

Опубликовал Stan Lo 23-12-2025

Больше новостей...