Вышел Ruby 3.2.0

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

Мы рады сообщить о выпуске Ruby 3.2.0. В Ruby 3.2 добавлено множество новых функций и улучшений производительности.

Поддержка WebAssembly на базе WASI

Это начальный порт поддержки WebAssembly на базе WASI. Он позволяет использовать бинарный файл CRuby в веб-браузере, в среде Serverless Edge или других типах встраиваемых систем WebAssembly/WASI. В настоящее время этот порт проходит базовые и загрузочные (bootstrap) наборы тестов без использования Thread API.

История вопроса

WebAssembly (Wasm) изначально был представлен для безопасного и быстрого запуска программ в веб-браузере. Но его цель — эффективное и безопасное выполнение программ в различных средах — давно востребована не только для веба, но и для обычных приложений.

WASI (The WebAssembly System Interface) разработан для таких случаев использования. Хотя таким приложениям необходимо взаимодействовать с операционными системами, WebAssembly работает на виртуальной машине, у которой не было системного интерфейса. WASI стандартизирует его.

Поддержка WebAssembly/WASI в Ruby призвана задействовать эти проекты. Она позволяет Ruby-разработчикам писать приложения, которые работают на этих перспективных платформах.

Варианты использования

Эта поддержка побуждает разработчиков использовать CRuby в среде WebAssembly. Примером использования является поддержка CRuby в песочнице TryRuby. Теперь вы можете попробовать настоящий CRuby прямо в своем веб-браузере.

Технические подробности

В современных WASI и самом WebAssembly отсутствуют некоторые функции для реализации Fiber, исключений и GC, так как они все еще развиваются, а также по соображениям безопасности. Поэтому CRuby восполняет этот пробел, используя Asyncify — технику преобразования бинарных файлов для управления выполнением в пространстве пользователя.

Кроме того, мы создали VFS поверх WASI, чтобы можно было легко упаковывать приложения Ruby в один файл .wasm. Это немного упрощает распространение Ruby-приложений.

Полезные ссылки

Готовый к промышленной эксплуатации YJIT

  • YJIT больше не является экспериментальным
    • Он тестировался на рабочих нагрузках в течение года и доказал свою стабильность.
  • YJIT теперь поддерживает процессоры x86-64 и arm64/aarch64 на Linux, MacOS, BSD и других UNIX-платформах.
    • В этот релиз вошла поддержка Apple M1/M2, AWS Graviton, Raspberry Pi 4 и других.
  • Для сборки YJIT теперь требуется Rust 1.58.0+. [Feature #18481]
    • Чтобы гарантировать, что CRuby собран с YJIT, пожалуйста, установите rustc >= 1.58.0 перед запуском скрипта ./configure.
    • Пожалуйста, свяжитесь с командой YJIT, если у вас возникнут какие-либо проблемы.
  • Релиз YJIT 3.2 быстрее, чем 3.1, и имеет примерно на треть меньше накладных расходов на память.
    • В целом YJIT на 41% быстрее (среднее геометрическое), чем интерпретатор Ruby на yjit-bench.
    • Физическая память для JIT-кода выделяется лениво. В отличие от Ruby 3.1, RSS процесса Ruby минимизирован, так как страницы виртуальной памяти, выделенные через --yjit-exec-mem-size, не будут отображаться на физические страницы памяти до тех пор, пока они действительно не будут использованы JIT-кодом.
    • Представлен Code GC, который освобождает все кодовые страницы, когда потребление памяти JIT-кодом достигает значения --yjit-exec-mem-size.
    • RubyVM::YJIT.runtime_stats возвращает метрики Code GC в дополнение к существующим ключам inline_code_size и outlined_code_size: code_gc_count, live_page_count, freed_page_count и freed_code_size.
  • Большинство статистических данных, генерируемых RubyVM::YJIT.runtime_stats, теперь доступны в релизных сборках.
    • Просто запустите ruby с параметром --yjit-stats, чтобы вычислить и вывести статистику (это влечет за собой небольшие накладные расходы во время выполнения).
  • YJIT теперь оптимизирован для использования форм объектов (object shapes). [Feature #18776]
  • Используется более мелкозернистая инвалидация констант, чтобы инвалидировать меньше кода при определении новых констант. [Feature #18589]
  • Значение по умолчанию для --yjit-exec-mem-size изменено на 64 (МиБ).
  • Значение по умолчанию для --yjit-call-threshold изменено на 30.

Улучшения Regexp для защиты от ReDoS

Известно, что сопоставление с регулярным выражением может занимать неожиданно много времени. Если ваш код пытается сопоставить потенциально неэффективное регулярное выражение с ненадежными входными данными, злоумышленник может использовать это для организации отказа в обслуживании (так называемый Regular expression DoS, или ReDoS).

Мы представили два улучшения, которые значительно снижают риск ReDoS.

Улучшенный алгоритм сопоставления регулярных выражений

Начиная с Ruby 3.2, алгоритм сопоставления Regexp был значительно улучшен благодаря использованию техники мемоизации.

# Это сопоставление занимает 10 сек. в Ruby 3.1 и 0.003 сек. в Ruby 3.2

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

Улучшенный алгоритм сопоставления позволяет выполнять большинство проверок регулярных выражений (около 90% в наших экспериментах) за линейное время.

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

Оригинальное предложение: https://bugs.ruby-lang.org/issues/19104

Таймаут Regexp

Вышеупомянутая оптимизация не может быть применена к некоторым типам регулярных выражений, например, содержащим расширенные функции (такие как обратные ссылки или просмотры вперед/назад) или с огромным фиксированным количеством повторений. В качестве резервной меры также введена функция таймаута для сопоставлений Regexp.

Regexp.timeout = 1.0

/^a*b?a*()\1$/ =~ "a" * 50000 + "x"
#=> Через одну секунду будет вызвано исключение Regexp::TimeoutError

Обратите внимание, что Regexp.timeout — это глобальная настройка. Если вы хотите использовать разные настройки таймаута для определенных регулярных выражений, вы можете использовать ключевое слово timeout в Regexp.new.

Regexp.timeout = 1.0

# Это регулярное выражение не имеет таймаута
long_time_re = Regexp.new('^a*b?a*()\1$', timeout: Float::INFINITY)

long_time_re =~ "a" * 50000 + "x" # никогда не прерывается

Оригинальное предложение: https://bugs.ruby-lang.org/issues/17837.

Другие заметные новые функции

SyntaxSuggest

  • Функция syntax_suggest (ранее dead_end) интегрирована в Ruby. Она помогает найти местоположение ошибок, таких как отсутствующие или лишние end, чтобы вы могли быстрее вернуться к работе, как показано в следующем примере:

    Unmatched `end', missing keyword (`do', `def`, `if`, etc.) ?
    
      1  class Dog
    > 2    defbark
    > 3    end
      4  end
    

    [Feature #18159]

ErrorHighlight

  • Теперь он указывает на соответствующие аргументы для TypeError и ArgumentError
test.rb:2:in `+': nil can't be coerced into Integer (TypeError)

sum = ary[0] + ary[1]
               ^^^^^^

Язык

  • Анонимные аргументы rest и keyword rest теперь можно передавать в качестве аргументов, а не только использовать в параметрах метода. [Feature #18351]

      def foo(*)
        bar(*)
      end
      def baz(**)
        quux(**)
      end
    
  • Proc, принимающий один позиционный аргумент и ключевые слова, больше не будет выполнять autosplat. [Bug #18633]

    proc{|a, **k| a}.call([1, 2])
    # Ruby 3.1 и ранее
    # => 1
    # Ruby 3.2 и позже
    # => [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+. Ранее флаг ruby2_keywords сохранялся, если принимающий метод принимал *args, но это было ошибкой и непоследовательностью. Хороший способ найти потенциально отсутствующие ruby2_keywords — запустить набор тестов, найти последний метод, который должен получать именованные аргументы для каждого места, где тесты не проходят, и использовать там puts nil, caller, nil. Затем проверьте, что каждый метод/блок в цепочке вызовов, который должен делегировать ключевые слова, правильно помечен как ruby2_keywords. [Bug #18625] [Bug #16466]

      def target(**kw)
      end
    
      # Случайно работало без ruby2_keywords в Ruby 2.7-3.1, ruby2_keywords
      # требуется в 3.2+. Точно так же (*args, **kwargs) или (...) потребуется
      # и в #foo, и в #bar при переходе от ruby2_keywords.
      ruby2_keywords def bar(*args)
        target(*args)
      end
    
      ruby2_keywords def foo(*args)
        bar(*args)
      end
    
      foo(k: 1)
    

Улучшения производительности

MJIT

  • Компилятор MJIT переписан на Ruby как ruby_vm/mjit/compiler.
  • Компилятор MJIT теперь выполняется в форкнутом процессе, а не в нативном потоке, называемом MJIT-воркером. [Feature #18968]
    • В результате Microsoft Visual Studio (MSWIN) больше не поддерживается.
  • MinGW больше не поддерживается. [Feature #18824]
  • Переименован параметр --mjit-min-calls в --mjit-call-threshold.
  • Значение по умолчанию --mjit-max-cache возвращено с 10000 к 100.

PubGrub

  • Bundler 2.4 теперь использует резолвер PubGrub вместо Molinillo.

    • PubGrub — это алгоритм решения следующего поколения, используемый пакетным менеджером pub для языка программирования Dart.
    • После этого изменения результаты разрешения зависимостей могут отличаться. Пожалуйста, сообщайте о таких случаях в RubyGems/Bundler issues
  • RubyGems в Ruby 3.2 все еще использует резолвер Molinillo. Мы планируем заменить его на PubGrub в будущем.

Другие заметные изменения по сравнению с 3.1

  • Data
    • Новый базовый класс для представления простых неизменяемых объектов-значений. Этот класс похож на Struct и частично разделяет его реализацию, но имеет более лаконичный и строгий API. [Feature #16122]

        Measure = Data.define(:amount, :unit)
        distance = Measure.new(100, 'km')            #=> #<data Measure amount=100, unit="km">
        weight = Measure.new(amount: 50, unit: 'kg') #=> #<data Measure amount=50, unit="kg">
        weight.with(amount: 40)                      #=> #<data Measure amount=40, unit="kg">
        weight.amount                                #=> 50
        weight.amount = 40                           #=> NoMethodError: undefined method `amount='
      
  • Hash
    • Hash#shift теперь всегда возвращает nil, если хеш пуст, вместо того чтобы возвращать значение по умолчанию или вызывать proc по умолчанию. [Bug #16908]
  • MatchData
  • Module
  • Proc
    • Proc#dup возвращает экземпляр подкласса. [Bug #17545]
    • Proc#parameters теперь принимает ключевое слово lambda. [Feature #15357]
  • Refinement
    • Добавлен метод Refinement#refined_class. [Feature #12737]
  • RubyVM::AbstractSyntaxTree
    • Добавлена опция error_tolerant для parse, parse_file и of. [Feature #19013] С этой опцией:
      1. SyntaxError подавляется
      2. AST возвращается для некорректного ввода
      3. end дополняется, когда парсер доходит до конца ввода, но end не хватает
      4. end обрабатывается как ключевое слово на основе отступа
        # Без опции error_tolerant
        root = RubyVM::AbstractSyntaxTree.parse(<<~RUBY)
        def m
          a = 10
          if
        end
        RUBY
        # => <internal:ast>:33:in `parse': syntax error, unexpected `end' (SyntaxError)
      
        # С опцией error_tolerant
        root = RubyVM::AbstractSyntaxTree.parse(<<~RUBY, error_tolerant: true)
        def m
          a = 10
          if
        end
        RUBY
        p root # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-4:3>
      
        # `end` обрабатывается как ключевое слово на основе отступа
        root = RubyVM::AbstractSyntaxTree.parse(<<~RUBY, error_tolerant: true)
        module Z
          class Foo
            foo.
          end
      
          def bar
          end
        end
        RUBY
        p root.children[-1].children[-1].children[-1].children[-2..-1]
        # => [#<RubyVM::AbstractSyntaxTree::Node:CLASS@2:2-4:5>, #<RubyVM::AbstractSyntaxTree::Node:DEFN@6:2-7:5>]
      
    • Добавлена опция keep_tokens для parse, parse_file и of. [Feature #19070]

        root = RubyVM::AbstractSyntaxTree.parse("x = 1 + 2", keep_tokens: true)
        root.tokens # => [[0, :tIDENTIFIER, "x", [1, 0, 1, 1]], [1, :tSP, " ", [1, 1, 1, 2]], ...]
        root.tokens.map{_1[2]}.join # => "x = 1 + 2"
      
  • Set
    • Set теперь доступен как встроенный класс, без необходимости вызова require "set". [Feature #16989] В настоящее время он подгружается автоматически через константу Set или вызов Enumerable#to_set.
  • String
    • Добавлены String#byteindex и String#byterindex. [Feature #13110]
    • Unicode обновлен до версии 15.0.0, а Emoji — до версии 15.0. [Feature #18639] (также относится к Regexp)
    • Добавлен метод String#bytesplice. [Feature #18598]
  • Struct
    • Класс Struct теперь также может быть инициализирован именованными аргументами без keyword_init: true в Struct.new. [Feature #16806]

        Post = Struct.new(:id, :name)
        Post.new(1, "hello") #=> #<struct Post id=1, name="hello">
        # Начиная с Ruby 3.2 следующий код также работает без keyword_init: true.
        Post.new(id: 1, name: "hello") #=> #<struct Post id=1, name="hello">
      

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

Примечание: Исключая исправления ошибок в функциях.

Удаленные константы

Следующие устаревшие константы были удалены.

Удаленные методы

Следующие устаревшие методы были удалены.

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

Сторонние исходные коды больше не поставляются в комплекте

  • Мы больше не поставляем в комплекте сторонние исходные коды, такие как libyaml, libffi.

    • Исходный код libyaml был удален из psych. Вам может потребоваться установить libyaml-dev в Ubuntu/Debian. Имя пакета отличается для каждой платформы.

    • Входящий в комплект исходный код libffi также удален из fiddle.

  • Psych и fiddle поддерживали статические сборки с конкретными версиями исходников libyaml и libffi. Вы можете собрать psych с libyaml-0.2.5 следующим образом:

      $ ./configure --with-libyaml-source-dir=/path/to/libyaml-0.2.5
    

    И вы можете собрать fiddle с libffi-3.4.4 следующим образом:

      $ ./configure --with-libffi-source-dir=/path/to/libffi-3.4.4
    

    [Feature #18571]

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

Обновленные C API

Следующие API были обновлены.

  • Обновление PRNG
    • rb_random_interface_t обновлен и получил версию. Библиотеки расширений, использующие этот интерфейс, должны быть собраны для старых версий. Также необходимо определить функцию init_int32.

Удаленные C API

Следующие устаревшие API были удалены.

  • Переменная rb_cData.
  • Функции “taintedness” и “trustedness”. [Feature #16131]

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

  • Bundler

    • Добавлена поддержка --ext=rust в bundle gem для создания простых гемов с расширениями на Rust. [GH-rubygems-6149]
    • Ускорена клонирование git-репозиториев [GH-rubygems-4475]
  • RubyGems

    • Добавлена поддержка mswin для cargo builder. [GH-rubygems-6167]
  • ERB

    • ERB::Util.html_escape теперь работает быстрее, чем CGI.escapeHTML.
      • Он больше не выделяет объект String, если не требуется экранирование символов.
      • Он пропускает вызов метода #to_s, если аргумент уже является строкой.
      • ERB::Escape.html_escape добавлен как псевдоним для ERB::Util.html_escape, который не подвергался monkey-patching со стороны Rails.
  • IRB

    • Добавлены команды интеграции с debug.gem: debug, break, catch, next, delete, step, continue, finish, backtrace, info.
    • Добавлено больше команд и функций в стиле Pry.
      • Добавлены команды edit и show_cmds (аналог help в Pry).
      • ls принимает опции -g или -G для фильтрации вывода.
      • show_source является псевдонимом для $ и принимает ввод без кавычек.
      • whereami является псевдонимом для @.
  • Следующие встроенные гемы были обновлены.

    • RubyGems 3.4.1
    • abbrev 0.1.1
    • benchmark 0.2.1
    • bigdecimal 3.1.3
    • bundler 2.4.1
    • cgi 0.3.6
    • csv 3.2.6
    • date 3.3.3
    • delegate 0.3.0
    • did_you_mean 1.6.3
    • digest 3.1.1
    • drb 2.1.1
    • english 0.7.2
    • erb 4.0.2
    • error_highlight 0.5.1
    • etc 1.4.2
    • fcntl 1.0.2
    • fiddle 1.1.1
    • fileutils 1.7.0
    • forwardable 1.3.3
    • getoptlong 0.2.0
    • io-console 0.6.0
    • io-nonblock 0.2.0
    • io-wait 0.3.0
    • ipaddr 1.2.5
    • irb 1.6.2
    • json 2.6.3
    • logger 1.5.3
    • mutex_m 0.1.2
    • net-http 0.3.2
    • net-protocol 0.2.1
    • nkf 0.1.2
    • open-uri 0.3.0
    • open3 0.1.2
    • openssl 3.1.0
    • optparse 0.3.1
    • ostruct 0.5.5
    • pathname 0.2.1
    • pp 0.4.0
    • pstore 0.1.2
    • psych 5.0.1
    • racc 1.6.2
    • rdoc 6.5.0
    • readline-ext 0.1.5
    • reline 0.3.2
    • resolv 0.2.2
    • resolv-replace 0.1.1
    • securerandom 0.2.2
    • set 1.0.3
    • stringio 3.0.4
    • strscan 3.0.5
    • syntax_suggest 1.0.2
    • syslog 0.1.1
    • tempfile 0.1.3
    • time 0.2.1
    • timeout 0.3.1
    • tmpdir 0.1.3
    • tsort 0.1.1
    • un 0.2.1
    • uri 0.12.0
    • weakref 0.1.2
    • win32ole 1.8.9
    • yaml 0.2.1
    • zlib 3.0.0
  • Следующие поставляемые гемы были обновлены.

    • minitest 5.16.3
    • power_assert 2.0.3
    • test-unit 3.5.7
    • net-ftp 0.2.0
    • net-imap 0.3.3
    • net-pop 0.1.2
    • net-smtp 0.3.3
    • rbs 2.8.2
    • typeprof 0.21.3
    • debug 1.7.1

Подробности о встроенных или поставляемых гемах смотрите в релизах на GitHub, например, релизы logger на GitHub, или в заметках о релизе.

Для получения подробной информации смотрите NEWS или журнал коммитов.

С учетом этих изменений было изменено 3048 файлов, добавлено 218253 строк(+), удалено 131067 строк(-) с момента выхода Ruby 3.1.0!

Счастливого Рождества, веселых праздников и приятного программирования на Ruby 3.2!

Скачать

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

    SIZE: 20440715
    SHA1: fb4ab2ceba8bf6a5b9bc7bf7cac945cc94f94c2b
    SHA256: daaa78e1360b2783f98deeceb677ad900f3a36c0ffa6e2b6b19090be77abc272
    SHA512: 94203051d20475b95a66660016721a0457d7ea57656a9f16cdd4264d8aa6c4cd8ea2fab659082611bfbd7b00ebbcf0391e883e2ebf384e4fab91869e0a877d35
    
  • https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.0.tar.xz

    SIZE: 15058364
    SHA1: bcdae07183d66fd902cb7bf995545a472d2fefea
    SHA256: d2f4577306e6dd932259693233141e5c3ec13622c95b75996541b8d5b68b28b4
    SHA512: 733ecc6709470ee16916deeece9af1c76220ae95d17b2681116aff7f381d99bc3124b1b11b1c2336b2b29e468e91b90f158d5ae5fca810c6cf32a0b6234ae08e
    
  • https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.0.zip

    SIZE: 24583271
    SHA1: 581ec7b9289c2a85abf4f41c93993ecaa5cf43a5
    SHA256: cca9ddbc958431ff77f61948cb67afa569f01f99c9389d2bbedfa92986c9ef09
    SHA512: b7d2753825cc0667e8bb391fc7ec59a53c3db5fa314e38eee74b6511890b585ac7515baa2ddac09e2c6b6c42b9221c82e040af5b39c73e980fbd3b1bc622c99d
    

Что такое Ruby

Ruby был впервые разработан Matz (Yukihiro Matsumoto) в 1993 году и сейчас развивается как open source. Он работает на многих платформах и используется по всему миру, особенно для веб-разработки.

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

Вышел Ruby 3.2.11

Вышел Ruby 3.2.11. В этот релиз вошло обновление гема zlib, устраняющее CVE-2026-27820.

Опубликовал hsbt 27-03-2026

Вышел Ruby 3.3.11

Вышел Ruby 3.3.11. В этот релиз вошло обновление гема zlib, устраняющее CVE-2026-27820, а также некоторые исправления ошибок.

Опубликовал hsbt 26-03-2026

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