Ruby 4.0 is here, releasing on Christmas Day 2025, marking 30 years since Ruby’s first public release. This release packs some genuinely exciting features.
Let’s explore the most impactful changes in Ruby 4.0.
ZJIT - A New JIT Compiler
Ruby 4.0 introduces ZJIT, a new just in time compiler built by the same team behind YJIT. Unlike YJIT’s lazy basic block versioning approach, ZJIT uses a more traditional method based compilation strategy.
The key difference? ZJIT is designed to be more accessible to contributors. It follows a “textbook” compiler architecture that’s easier to understand and modify.
To enable ZJIT, we can use the --zjit flag:
ruby --zjit my_script.rbWhile ZJIT is faster than interpreted code, YJIT remains the recommended choice for production. ZJIT sets the foundation for future performance improvements and community contributions.
Ruby::Box - Isolated Namespaces
Ruby::Box is an experimental feature that brings isolated namespaces to Ruby. This allows us to load multiple versions of a library simultaneously.
Enable it with the RUBY_BOX=1 environment variable:
# Load two different versions of a gem
box1 = Ruby::Box.new
box1.require("some_gem", version: "1.0")
box2 = Ruby::Box.new
box2.require("some_gem", version: "2.0")This is particularly useful for:
- Testing library upgrades without conflicts
- Running legacy code alongside modern dependencies
- Isolating third party code
The syntax is still evolving, so expect improvements in future releases.
Redesigned Ractor API
Ractors, Ruby’s answer to true parallelism, received a significant API overhaul.
The new design uses Ractor::Port for communication between ractors.
# Ruby 4.0 - New Ractor::Port API
port = Ractor::Port.new
r = Ractor.new(port) do |p|
p.send("Hello from Ractor!")
end
message = port.receive
puts messageThe old Ractor.yield
and Ractor#take methods have been removed.
This change mirrors inter process communication (IPC) semantics,
making the API more intuitive for developers familiar with concurrent programming patterns.
Logical Operators on Next Line
Ruby 4.0 now allows logical operators to appear at the beginning of a new line:
# Now valid in Ruby 4.0
result = condition_one
&& condition_two
&& condition_three
# Also works with 'and', 'or', '||'
value = first_option
|| second_option
|| default_valueThis improves readability for complex conditional expressions and aligns with common formatting preferences.
instance_variables_to_inspect
When debugging, inspect can get noisy with memoization variables.
Ruby 4.0 introduces instance_variables_to_inspect to control what shows up:
class Rectangle
def initialize(width, height)
@width = width
@height = height
end
def area
@area ||= @width * @height
end
def instance_variables_to_inspect
[:@width, :@height]
end
end
rect = Rectangle.new(10, 5)
rect.area
puts rect.inspect
# => #<Rectangle @width=10 @height=5>
# @area is hidden from outputThis keeps inspect output clean
and focused on the essential state.
Set Is Now a Core Class
Set has been promoted from stdlib to a core class.
No more require 'set' needed:
# Works directly in Ruby 4.0
fruits = Set["apple", "banana", "cherry"]
fruits.add("mango")
# inspect now returns eval friendly output
fruits.inspect
# => Set["apple", "banana", "cherry", "mango"]
# Previously: #<Set: {"apple", "banana", "cherry", "mango"}>Note: SortedSet has been removed.
So we will need to install the sorted_set gem if/when required.
Pathname Promoted to Core
Pathname is now a core class instead of a default gem.
This means better integration
and no separate gem management:
# Available without require
path = Pathname.new("/home/user/documents")
path.children.each { |child| puts child.basename }Extended source_location
Method#source_location, Proc#source_location,
and UnboundMethod#source_location now return 5 elements instead of 2:
def greet(name)
"Hello, #{name}!"
end
method(:greet).source_location
# => ["/path/to/file.rb", 1, 0, 2, 3]
# [path, start_line, start_column, end_line, end_column]
# For backward compatibility
method(:greet).source_location.take(2)
# => ["/path/to/file.rb", 1]This is great for tooling and IDE integrations that need precise location info.
String#strip with Selectors
String#strip and its variants now accept selector arguments:
text = " \t Hello World \n "
# Strip specific characters
text.strip(" ", "\t", "\n")
# => "Hello World"New Math Methods
Ruby 4.0 adds Math.log1p
and Math.expm1 for more precise calculations with small numbers:
# log1p(x) computes log(1 + x) more accurately for small x
Math.log1p(0.0001)
# => 0.00009999500033330834
# expm1(x) computes exp(x) - 1 more accurately for small x
Math.expm1(0.0001)
# => 0.00010000500016667084These are essential for scientific computing and financial calculations.
Other Notable Changes
*nilno longer callsnil.to_a(consistent with**nilbehavior)IO.selectacceptsFloat::INFINITYas timeoutFile::Stat#birthtimenow works on Linux (via statx syscall)Socket.tcpacceptsopen_timeout:keyword argumentRactor#joinandRactor#valueadded (similar to Thread)- RJIT has been removed (YJIT and ZJIT remain)
- Unicode updated to version 17.0
Wrapping Up
Ruby 4.0 is a significant release that lays groundwork for the future. ZJIT opens doors for community driven compiler improvements, Ruby::Box experiments with namespace isolation, and the Ractor redesign makes parallel programming more intuitive.
For most applications, upgrading should be smooth since there are no major breaking changes.
References:
