IronRuby 4.0.5: Ruby 4.0 on .NET 10 and .NET 11
At the end of September I wrote about IronRuby 4.0, my fork that brings IronRuby to Ruby 4.0, with prism as its parser. Back then it ran on .NET 8 and the first download was called 4.0.0-preview1. Six weeks and six releases later, IronRuby 4.0.5 is out. It is the first release that runs on .NET 10 and .NET 11, and the first without .NET 8.
$ ./ironruby -e 'puts RUBY_DESCRIPTION'
IronRuby 4.0.5 (4.0.0) on .NET 10.0.15 [x86_64-linux]
Why .NET 8 had to go
Microsoft’s support for .NET 8 ended yesterday, on 10 November 2026. 4.0.4 was the last release for it. From 4.0.5 on, IronRuby builds for net10.0 and net11.0, and the code that only .NET 8 needed is removed.
Each download now comes in two builds:
- .NET 10, the default. The files keep the names from earlier releases, like
ironruby-4.0.5-linux-x64.tar.gz, and the Windows installer uses this build too. .NET 10 is an LTS release, supported until 14 November 2028. - .NET 11, with
-net11-in the name, for Linux, Windows and macOS. There is no installer for it.
Both carry their own runtime, so you don’t need to install .NET to run Ruby scripts. Only a program that hosts IronRuby, or loads its assemblies, now needs .NET 10 or later.
On .NET 11, startup is about 5% shorter, and TruffleRuby’s benchmark collection runs about 3.5% faster. The default stays on .NET 10 because it is the LTS release.
Two small things change with the runtime. An Integer can no longer be wider than about 2**31 bits, because that is the limit of .NET’s BigInteger since .NET 9. 1 << 2**31 now raises NoMemoryError, as CRuby does when an allocation fails. And on Windows, zlib now uses the zlib-ng inside .NET, so Zlib::ZLIB_VERSION changes and compressed bytes can differ from 4.0.4. They still inflate the same.
What happened since September
4.0.5 itself is a small release. The bigger changes came in the four releases before it. Here are the ones I find most important.
Rails with SQLite, also on Windows
Sam Ruby (@rubys) started running Rails apps that his Roundhouse project transpiles, and found the first bugs: pipes and sockets on macOS, where Puma could not start, IO#write_nonblock, which cut large responses short, and a SQLite crash on Linux. All three were fixed in 4.0.1.
In 4.0.2, a Rails 8.1 app runs with SQLite on Windows: gem install rails, rails new, a scaffold, migrations, Turbo and Stimulus, and bin/rails server on Puma. In a test of popular gems on Windows, 81 of them gave the same output as CRuby, byte for byte, among them rubocop, rspec, pry and prawn. TLS works too, both in net/http and in Puma.
A Windows installer and irb
Since 4.0.3 there is a Windows installer in the style of RubyInstaller. It installs into C:\IronRuby40-x64, works without admin rights and puts ironruby, iirb and igem on the PATH. irb and gem stay CRuby’s, so both can live side by side.
irb now works on Windows as it does in CRuby, with colours, type-based completion and correct non-ASCII text in the console. And the interpreter is called ironruby now, as JRuby’s is jruby. Started at a terminal without a script, it opens irb.
.NET the way Ruby 4 is written
Calling .NET got a lot nicer. Keyword arguments bind to .NET parameters by name, and a block goes to a delegate parameter, so list.Sort { |a, b| b <=> a } and Task.Run { } just work.
Thanks to @sholden, using also takes a .NET namespace now, like C#’s using directive. It brings in its extension methods for the rest of the file only:
using System::Linq
list = System::Collections::Generic::List[Integer].new([3, 1, 2])
p list.where { it > 1 }.order_by { -it }.to_a # => [3, 2]
The 101 LINQ samples run in CI.
Much faster
4.0.4 was mostly about speed. A benchmark sweep had listed the places where IronRuby was far behind CRuby, and most of them are now closed or much smaller:
- A rescued exception costs about a quarter of what it did.
- Copying a long String takes constant time:
s.dupof 100 KB went from 83 to 0.3 µs. RationalandComplexare written in C#:Rational(1, 3)went from 540 to 49 ns, faster than CRuby.- ruby-bench’s lobsters runs 31 to 38% faster, and protoboeuf’s decode went from 81 s to 1.3 s.
- On a Doom renderer in Ruby, a frame takes 16 to 18 ms instead of about 27 ms. CRuby without YJIT needs 22 to 25 ms.
Raising an exception is still the weak spot: it costs tens of microseconds, against 1 to 6 in CRuby, because of how .NET handles exceptions.
Closer to CRuby
A few more things that make IronRuby behave like CRuby 4.0:
- Deep recursion raises a
SystemStackErrorthat you can rescue. Before, it ended the process. Ractoris there, with Ruby 4.0’s API. 146 of the 153 checks in CRuby’s Ractor tests pass.- Error messages look like CRuby’s, including error_highlight and prism’s annotated syntax errors.
- Error messages quote names
'like this', as CRuby has done since 3.4. - More gems with C extensions work, because their extension is ported to C#, like strptime, yajl-ruby and zstd-ruby. 34 of ruby-bench’s 36 larger benchmarks run.
Thank you
IronRuby got its first contributors from outside. Thank you to Sam Ruby for the macOS and socket fixes, to @khasinski for File#flock on macOS and ir -S, and to @sholden for refinements, LINQ, threads and much more.
Trying it
The release page has the downloads for Linux x64, Windows x64 and macOS on Apple silicon. On Windows the easiest way is the installer, ironruby-4.0.5-win-x64-setup.exe. Elsewhere, unpack the archive:
$ tar xzf ironruby-4.0.5-linux-x64.tar.gz
$ cd ironruby-4.0.5-linux-x64
$ ./ironruby
$ ./ironruby script.rb
$ ./igem.sh install rack
Every file has a signed build provenance attestation, so you can check which workflow built it from which commit:
$ gh attestation verify ironruby-4.0.5-linux-x64.tar.gz --repo Largo/ironruby
The macOS builds are made in CI, but I still haven’t really tested them. If you try IronRuby and something breaks, please open an issue or write to me.