This document is a list of user-visible feature changes since the 4.0.0 release, except for bug fixes.
Note that each entry is kept to a minimum, see links for details.
Module#cloneandModule#dupno longer rewrite the lexical scope of copied methods. Constants and class variables resolve through the original class, consistent with inheritance and mixins. [Feature #21981]
Note: We're only listing outstanding class updates.
-
C API
RB_NOGVL_PENDING_INTR_FAILis added as a flag forrb_nogvl.rb_nogvldoes not enter the blocking region (and does not call the given function), returning0, if the current thread has pending interrupts, including interrupts masked byThread.handle_interrupt. As with the existing skip path,errnois0because the function was never called.
-
Array
Array#packaccepts new formatsRandrfor unsigned and signed LEB128 encoded integers. [Feature #21785]Array#packaccepts new formatsx!and@!to align the current offset to a byte boundary or to the ABI alignment of another directive. [Feature #22185]
-
ENV
ENV.fetch_valuesis added. It returns an array of values for the given names, raisingKeyErrorfor missing names unless a block is given. [Feature #21781]
-
Fiber::Scheduler
- The
fiber_interrupthook is now required. Schedulers which do not implement it can no longer be installed withFiber.set_scheduler.
- The
-
Hash
Hash.ruby2_keywords_hash?andHash.ruby2_keywords_hashare deprecated and will be removed in Ruby 4.5. [Feature #22205]
-
Integer
Integer#bit_countis added. It returns the number of1bits in the binary representation of a non-negative integer (its population count). [Feature #20163]
-
IO::Buffer
-
read,write,pread, andpwritenow perform one IO operation using(offset, length), wherelengthis the maximum transfer size. Short transfers are returned directly. -
The corresponding fiber scheduler hooks are no longer experimental and now use the same
(offset, length)argument order and single-operation semantics.
-
-
Kernel
Kernel#autoload_relativeandModule#autoload_relativeare added. These methods work likeautoload, but resolve the file path relative to the file where the method is called, similar torequire_relative. This makes it easier to autoload constants from files in the same directory without hardcoding absolute paths or manipulating$LOAD_PATH. [Feature #15330]
-
MatchData
MatchData#integer_atis added. It converts the matched substring to integer and return the result. [Feature #21932]
-
Module
Module#descendantsis added. It returns an array of classes and modules that have the receiver in their ancestors. [Feature #9779]Module#ruby2_keywordsand top-levelruby2_keywordsare deprecated and will be removed in Ruby 4.4. [Feature #22205]
-
ObjectSpace
ObjectSpace._id2refwas removed. [Feature #22135]
-
Proc
Proc#refinedis added. It returns a newProcthat behaves like the receiver but with the refinements activated by the given modules in effect inside its body, without affecting the originalProc. [Feature #22097]Proc#ruby2_keywordsis deprecated and will be removed in Ruby 4.4. [Feature #22205]
-
Range
Range#clampis added. It returns a newRangeinstance whose begin and end values are clamped to the given bounds. [Feature #22175]
-
Regexp
- All instances of
Regexpare now frozen, not just literals. Subclasses ofRegexpare not frozen for compatibility. [Feature #8948]
- All instances of
-
Set
- A deprecated behavior,
Set#to_set,Range#to_set, andEnumerable#to_setaccepting arguments, was removed. [Feature #21390]
- A deprecated behavior,
-
String
String#unpackandString#unpack1accept new formatsRandrfor unsigned and signed LEB128 encoded integers. [Feature #21785]String#unpackandString#unpack1accept a new format^that returns the current offset. Useful when combined with variable width formats like LEB128. [Feature #21796]String#unpackandString#unpack1accept new formatsx!and@!to align the current offset to a byte boundary or to the ABI alignment of another directive. [Feature #22185]- Basic bit operations are added.
String#bit_get,String#bit_set?,String#bit_set,String#bit_clear,String#bit_flipandString#bit_counthandle individual bits, andString#bitwise_not,String#bitwise_and,String#bitwise_or,String#bitwise_xor(with their!variants) handle whole strings. [Feature #22118] String#trnow accept a Hash for multi-character replacement. [Feature #22238]
-
Symbol
Symbol#to_snow returns a frozen string. [Feature #22137]
-
Thread::Backtrace::Location
Thread::Backtrace::Location#source_rangeis added. It returns aRuby::SourceRangefor the Ruby expression associated with the frame.
-
Psych
- An experimental libfyaml backend has been added. It is only
enabled when psych is built with
--enable-libfyaml, and the default libyaml backend remains unchanged otherwise. It is not supported on Windows. [GH-psych #805]
- An experimental libfyaml backend has been added. It is only
enabled when psych is built with
We only list stdlib changes that are notable feature changes.
Other changes are listed in the following sections. We also listed release history from the previous bundled version that is Ruby 3.4.0 if it has GitHub releases.
- tsort 0.2.0
- win32-registry 0.1.2
- net-ftp 0.3.9
- net-pop 0.1.2
They are still available on rubygems.org and can be installed with
gem install. [Feature #21835]
- RubyGems 4.1.0.beta1
- bundler 4.1.0.beta1
- erb 6.0.7
- error_highlight 0.7.2
- io-console 0.9.2
- ipaddr 1.2.9
- 1.2.8 to v1.2.9
- json 3.0.2
- net-protocol 0.3.0
- 0.2.2 to v0.3.0
- openssl 4.0.2
- pp 0.6.4
- 0.6.3 to v0.6.4
- prism 1.9.0
- psych 5.5.0
- resolv 0.7.1
- 0.7.0 to v0.7.1
- stringio 3.2.1.dev
- strscan 3.1.9.dev
- syntax_suggest 3.0.0
- timeout 0.6.1
- 0.6.0 to v0.6.1
- zlib 3.2.3
- 3.2.2 to v3.2.3
- minitest 6.0.6
- power_assert 3.1.0
- 3.0.1 to v3.1.0
- rake 13.4.2
- test-unit 3.7.8
- rss 0.3.3
- 0.3.2 to 0.3.3
- net-imap 0.6.7
- rbs 4.2.0
- 3.10.0 to v3.10.1, v3.10.2, v3.10.3, v3.10.4, v4.0.0.dev.1, v4.0.0.dev.2, v4.0.0.dev.3, v4.0.0.dev.4, v4.0.0.dev.5, v4.0.0, v4.0.1.dev.1, v4.0.1.dev.2, v4.0.1, v4.0.2, v4.0.3, v4.1.0.pre.1, v4.1.0.pre.2, v4.1.0, v4.1.1.pre.1, v4.1.1, v4.1.2, v4.1.3, v4.2.0.pre.1, v4.2.0
- typeprof 0.33.1
- mutex_m 0.3.0
- bigdecimal 4.1.2
- resolv-replace 0.2.0
- 0.1.1 to v0.2.0
- nkf 0.3.0
- 0.2.0 to v0.3.0
- syslog 0.4.0
- 0.3.0 to v0.4.0
- csv 3.3.6
- 3.3.5 to v3.3.6
- repl_type_completor 0.1.16
- pstore 0.2.1
- 0.2.0 to v0.2.1
- rdoc 8.0.0
- win32ole 1.9.3
- 1.9.2 to v1.9.3
- irb 1.18.0
- reline 0.7.0
- 0.6.3 to v0.7.0
Ruby 4.0 bundled RubyGems and Bundler version 4. see the following links for details.
-
Support code for the following platforms has been removed. Note that all of them reached end of life many years ago and Ruby has long been unbuildable on them.
- Interix (Windows Services for UNIX)
- SunOS 4 (Solaris, i.e. SunOS 5, is unaffected)
- BSD/OS (BSDi)
-
Windows 10 1703 or later no longer needs the
LongPathsEnabledregistry value to use paths longer than 260 characters. This applies to any process running the interpreter, including a program which embeds libruby. Each path component is still limited to 255 characters, and a child process still starts with theMAX_PATHlimited current directory. [Bug #18947]
-
A class or module can now be modified only by the Ractor which created it, its owner. Defining, removing or undefining methods,
alias, changing visibility,include/prepend,Module#refine, defining or removing constants, registering anautoload, writing the class's own instance variables and class variables,Module#freezeandModule#set_temporary_nameraiseRactor::IsolationErrorin any other Ractor. Reading is unchanged. Everything defined at boot or by the main Ractor,required libraries included, is owned by the main Ractor, so a non-main Ractor can no longer monkey-patch it; and since defining a constant in a foreign class is prohibited, it can not define a top-level class or module name either. In exchange a Ractor has full use of the classes it creates itself, including unshareable constant, instance variable and class variable values, which not even the main Ractor could do before. See doc/language/ractor.md. [Feature #22226] -
Kernel#at_exitandEND {}now raiseRactor::IsolationErrorwhen called in a non-main Ractor. Previously the registered handler ran in the main Ractor at process exit, which was confusing. [Feature #22139] -
Ractor.make_shareablenow raisesRactor::Errorfor anIOobject, andRactor.shareable?returnsfalsefor it. A frozenIOused to be shareable, but almost all of its methods raiseFrozenErrorand it can still refer to unshareable objects through the members ofrb_io_t.
Etc.getloginon Windows now returns the login name determined when the process starts, from theUSERorUSERNAMEenvironment variable orGetUserName(). It used to follow later changes toENV['USER'].
-
The
RUBY_TYPED_EMBEDDABLEflag is now public and documented and can be used by C extensions. It allows allocating C structs directly into Ruby object slots, which reduces pointer chasing, and in some case memory usage. See the C extension documentation for details. [Feature #21853] -
Added new C23 inspired allocator functions, that takes the previous memory size. This allow the Ruby GC to better keep track of memory usage, improving its heuristics. It also improves the performance of system allocators that support C23
free_sized.However, it is important to note that passing an incorrect size to these function is undefined behavior and may result in crashes or memory leaks.
ruby_xfree_sized(void *ptr, size_t size)ruby_xrealloc_sized(void *ptr, size_t newsiz, size_t oldsiz)ruby_xrealloc2_sized(void *ptr, size_t newelems, size_t newsiz, size_t oldelems)
The following APIs, which have been deprecated for many years, are removed. [Feature #21768]
- old postponed job functions,
- untyped data object type/functions,
- old APIs to allocate a data object,
- taintedness/trustedness enums/macros,
rb_gc_force_recyclefunction,rb_iteratefunction,- and some functions and constants for internal use.
-
The default garbage collector has been switched from a freelist allocator to a bump pointer allocator. [PR #17201]
-
The error_highlight, did_you_mean, and syntax_suggest gems are now loaded lazily on the first error display instead of at interpreter boot, which reduces startup time.
Process.warmuploads them eagerly so that pre-forking servers keep their code in copy-on-write shared memory. [Feature #21951]
A lot of work has gone into making Ractors more stable, performant, and usable. These improvements bring Ractor implementation closer to leaving experimental status.
-
The default GC now runs per Ractor: each Ractor collects its own heap on its own thread without stopping the others, and a stop-the-world collection only runs when it is really needed (explicit full
GC.start, shareable-object growth, reclaiming dead Ractors' heaps). Allocation-heavy Ractor programs now scale like forked processes.Visible behavior changes:
Ractor#valuereturns the value only once; a second call raisesRactor::Error.GC.disable/GC.enableact as per-Ractor holds on a process-wide switch: one Ractor'sGC.enableno longer overrides another Ractor'sGC.disable.ObjectSpace.each_objectenumerates the calling Ractor's own objects plus other Ractors' shareable objects (ObjectSpace.dump_allstill covers everything).ObjectSpace.define_finalizeron another Ractor's object raisesRactor::IsolationError.
-
The scheduler scales with the number of waiters and of Ractors, where it used to walk a list or take one lock for all of them:
- A timed wait sits in a hierarchical timer wheel rather than on a list sorted by deadline, which was inserted into by a linear scan.
- An fd stays armed in the backend between waits, instead of being added before each wait and removed after each wake.
- The io-wait bookkeeping is sharded by fd, rather than serialized on one lock across every fd.
- A timed wait on an fd rides the scheduler instead of going to a blocking region, which cost a native thread handoff per wait.
- A context switch, and leaving or rejoining the shared pool, no longer take the scheduler's global lock.
-
The
RUBY_MN_THREADSenvironment variable now runs from no M:N scheduling at all to all of it.-1is new: a Ractor's threads have been M:N since the scheduler was added, with no way to turn that off.0and1are unchanged.main thread the main Ractor's other threads a Ractor's threads -11:1 1:1 1:1 0or unset1:1 1:1 M:N 11:1 M:N M:N 2M:N M:N M:N -
RUBY_MN_THREADS=2is new. The main thread is resumed like any other M:N thread rather than woken on a native thread of its own, which costs an order of magnitude more. It pays off when the main thread drives the work, and does nothing for one that only starts other threads and waits.The main thread is then no longer bound to one OS thread, which is what the M:N scheduler already meant for every other thread:
- A C extension that keeps state per OS thread has to call
rb_thread_lock_native_thread(). - What must run on the process's initial thread does not work at all, pinning included: macOS AppKit and CFRunLoop, and hosts that embed Ruby and return into their own main loop.
- A C extension that keeps state per OS thread has to call
-
The OS thread name is no longer set from the Ruby thread for M:N threads: one native thread runs many of them over its life.
Thread#name=was already skipped for the same reason.