Written by James BrittTechnically reviewed by Jim Freeze · Reviewed
Ruby 4.0 new features at a glance
Ruby 4.0 new features include reverse array search, character selectors for string trimming, a core Set class, experimental Ruby Box isolation and the experimental ZJIT compiler. Ractor also gains a Port API for messages. You can try some changes in a short script; the runtime facilities need testing in your application’s environment.
Use a maintained Ruby 4.0 patch release for your own trial. The collection examples here passed checks in Ruby 4.0.0 using the official WebAssembly build. Those checks reproduce the small examples. They do not measure a native application’s performance.

| Feature | What changed | A useful first question |
|---|---|---|
| Array#rfind | Search an array from its end for a matching element. | Does the last matching record have meaning in this ordering? |
| String#strip selectors | Choose characters to remove from the ends. | Am I trimming characters or removing a whole token? |
| Set in core | Set becomes a core class. | Do I need uniqueness, ordering or both? |
| Ruby Box | Experimental separation of definitions within a process. | Which definitions or patches need separation? |
| ZJIT | A new experimental JIT compiler. | What does the actual workload measure? |
| Ractor::Port | Explicit ports for messages between Ractors. | Who receives, and who closes the channel? |
Try the collection changes first
Arrays maintain a particular order. Sometimes you need the last item that meets a condition. Ruby 4.0 adds Array#rfind for that search. It starts from the right and returns the first qualifying element it encounters.
values = [5, 2, 8, 4]
values.rfind { |value| value > 5 }
# => 8
You receive one matching item, rather than a list of every match. If no item qualifies, the result is nil. The original array keeps its order, which matters when other code continues to use the same collection.
Decide what the order means before choosing the search. The final entry in an array does not inherently represent the newest record or the highest value. Searching backwards through records imported in a random order does not make those records a historical sequence.
For a project that also supports Ruby 3.4, compare this with reverse_each.find. Wait until your supported runtimes include rfind before adopting it. Keep the expected answer explicit in a test so a shorter spelling does not inadvertently change the business rule.
Trim selected characters, not an exact wrapper
String#strip normally removes leading and trailing whitespace. Ruby 4.0 also lets it accept character selectors. This example selects angle brackets for trimming while leaving the letters between them intact.
label = "<<Ruby>>"
label.strip("<>")
# => "Ruby"
label
# => "<<Ruby>>"
The non-bang method creates a new string without changing the original label. A selector describes characters, rather than an exact token such as an opening tag. If you need to remove one known prefix, define that requirement separately from trimming selected characters at either end.
Try another input with an angle bracket in the middle. Trimming only affects the boundaries, so the interior bracket remains. That extra check tells you more than testing only the tidy input that first motivated the code.
Selectors also support character ranges and other selection forms. Consult the current method documentation before using a complicated pattern. For ordinary whitespace trimming, the argument-free call remains clear.
Set joins the core classes
Set becomes a core class rather than an autoloaded standard-library class. It still models a collection in which duplicate members do not belong. In this example, two occurrences of the same integer contribute one member.
numbers = Set[2, 2, 5]
numbers.size
# => 2
numbers.include?(5)
# => true
Choose the data structure for the data. An array can be a better fit when positions or repeated values matter. A set expresses uniqueness and does not supply a missing sorting rule for your application.
Ruby 4.0 also changes Set’s inspection display. A test that compares a printed representation might be checking an actual interface requirement, or it might merely depend on a convenient debugging string. Where possible, assert membership and size directly.
Ruby Box separates definitions within a process
Ruby Box provides experimental spaces for definitions such as classes, modules, libraries and monkey patches. The official Ruby 4.0 release announcement introduces it alongside ZJIT and the other major changes.
Imagine a test that modifies a method and accidentally affects a later test. This is a problem of definition separation. The useful question becomes which version of a definition a piece of code should see, rather than merely which version of some data it should read.
Enable Ruby Box when starting the process. Treat it as an experiment with your dependency set, especially when native extensions and frameworks are involved. The documentation lists known issues; a successful isolated example does not establish compatibility with a whole application.
A Box is not an operating-system security boundary. Putting untrusted code in one does not make that code safe to execute. Evaluate it for definition isolation and keep track of what still shares the process.
ZJIT and Ractor need workload-specific evaluation
ZJIT is an experimental just-in-time compiler introduced in Ruby 4.0. Adding a compiler does not necessarily make every program faster. The 4.0.0 announcement described ZJIT as faster than the interpreter but not yet as fast as YJIT, and advised experimentation rather than immediate production deployment.
Keep the comparison tied to that release. Later patches and future Ruby branches can change it. Test a representative workload with a documented runtime build and configuration before making a performance claim about your application.
Ractor changes message handling too. Ractor::Port represents a channel between Ractors. The Ractor that created a port can receive messages through it and close it. Older message-passing examples may need updating before they work in a 4.0 project.
A single successful run does not cover concurrency behaviour. Check empty queues, closed channels, worker failures and shutdown. This guide’s examples do not execute Ractor concurrency or benchmark JIT compilers; the runtime checks cover the pure collection operations shown above.
Predict the results of the Ruby 4.0 new features
Before opening the answer, change one input in each collection example. Replace the reverse-search condition with a value above ten. Put an angle bracket inside a label, then count unique integers in a list containing duplicates.
What should those three checks show?
The search returns nil when nothing matches. Trimming selected brackets leaves an interior bracket in place. A Set keeps one member for each distinct integer. These are separate rules: search direction, string boundaries and uniqueness. The accompanying code checks verify them independently.
If the syntax is unfamiliar, our Ruby programming language overview explains how values, arrays, blocks and return values work together. Understanding the returned object helps more than memorising a new method name.
Check compatibility before adopting the new helpers
The Ruby 4.0 NEWS lists user-visible updates, including removals and library packaging changes. Use it to identify changes that affect your project. A feature overview still needs an API review against the code you actually run.
First make the existing application work under the chosen runtime. Once it works, add a helper where it makes the code easier to read. Keep experimental runtime work in separate commits so a failure has fewer possible causes.
Which Ruby versions must a shared library support?
Your development machine may use Ruby 4.0 while some people using the library still run 3.4. Adding a new method call adds a requirement for those users. Test both the oldest supported runtime and the newer one, and make the declared Ruby requirement match the library’s code.
Passing tests on your own machine does not establish continued support for older Ruby versions. Keep that support check visible in CI. It is easier to find an unsupported method there than after somebody installs a released gem.
You do not need to turn on every optional facility to use a collection helper. Ruby Box needs a startup setting; ZJIT depends on the runtime build and configuration. Record which optional features a test actually enables. Otherwise, two developers may compare results from different configurations while believing they ran the same test.
Ruby 4.0 new features solve different kinds of problems. A collection helper can clarify a short expression. Definition separation, a compiler and concurrency facilities need a more deliberate trial. Choose a change whose purpose and expected result you can explain.
