Nithin Bekal Posts About

What's new in Ruby 3.1?

24 Jan 2022

The Ruby core team has a tradition of releasing a new version of Ruby on Christmas day, and last month we got Ruby 3.1.0. This release brings a new JIT compiler, a new debugger, improvements to IRB, better error messages, new hash syntax and lots of other improvements.

YJIT

The big news this year is YJIT, the new JIT compiler developed at Shopify. This has been merged into Ruby and is available using the --yjit flag. Ruby already has another JIT called MJIT but it hasn’t translated to performance improvements in real world scenarios. YJIT has shown more promise with realistic workloads like Rails codebases, with over 20% faster performance in railsbench.

Shorthand hash syntax

New syntactic sugar has been added, which allows omitting of the keys of a hash, or the keyword argument values for a method, when the keys have the same name as a variable or method in scope. There have been a few instances where I’ve wished for something like this recently, so I’m glad to see this syntax.

a = 1
b = 2

# Old
{ a: a, b: b } #=> { a: 1, b: 2}

# Ruby 3.1
{ a:, b: } #=> { a: 1, b: 2 }

This can be especially useful with keyword arguments:

# Old
foo(a: a, b: b)

# 3.1
foo(a:, b:)

Anonymous block forwarding

If you need to pass a block argument to another method, it no longer needs to be named - you can just use & to refer to it. Here’s a simple example of how you might use it:

def partition(list, &)
  selected = list.select(&)
  rejected = list.reject(&)

  [selected, rejected]
end

evens, odds = partition([1,2,3,4,5]) { |n| n.even? }

evens #=> [2,4]
odds  #=> [1,3,5]

Pin operator improvements

When pattern matching, the pin operator now supports expressions, including instance or class variables, and constants.

x = 42

case n
in ^x
  puts "equal"
in ^(x * 2)
  puts "double"
end

Class#subclasses

This new method returns the direct subclasses of the class. In the example below, Baz is not included in Foo.subclasses because it doesn’t directly inherit from Foo.

class Foo; end
class Bar < Foo; end
class Baz < Bar; end

Foo.subclasses #=> [Bar]

New debugger

Ruby now comes bundled with the debug gem, a new debugger that includes many features that are provided by gems like pry and byebug. It can be invoked using the rdbg executable (example rdbg foo.rb) or you can add breakpoints similar to pry by using binding.break. I’m a heavy user of pry, so I’m excited to see if rdbg can provide enough features of pry to be able to replace it in my workflow.

Better error messages

Ruby now comes bundled with a gem called error_highlight, which shows much better error messages. This is especially useful with those “undefined method x on NilClass” messages. For instance, in the example below, we can easily identify that the nil value comes when we invoke [] on the variable json rather than on json[:article].

$ ruby test.rb
test.rb:2:in `<main>': undefined method `[]' for nil:NilClass (NoMethodError)

title = json[:article][:title]
            ^^^^^^^^^^

IRB improvements

IRB now supports autocomplete, and even shows the documentation when you tab through the options. For instance, you could type "" followed by a dot, and you will see an autocomplete menu. If you navigate to it using Tab, you can see a snippet next to it. You can also expand the documentation snippet by hitting Alt+d.

Final thoughts

This has been a great release for the Ruby community. Matz mentioned in his talk at Rubyconf 2021 that Ruby will now focus on implementation, performance and tooling rather than language enhancements for the near future, and this is evident in Ruby 3.1.

YJIT has already brought decent peformance improvements, and promises even more in the future. The new debugger and IRB improvements means that we have a great alternative to pry bundled with Ruby.

Although it’s been only a month since the release, next December’s Ruby 3.2 is already looking interesting. WebAssembly support has already been merged in. There are plans to rewrite YJIT in Rust, which could mean faster development on the JIT. These are exciting days for the Ruby community!

Further reading

For my posts about previous versions of Ruby, see here: 3.0, 2.7, 2.6, 2.5, 2.4, 2.3.

With these posts, I try to highlight some of the changes that I found most interesting, and often skip over features I might not use. If you’re looking for a more comprehensive look at the release, I highly recommend looking at the release announcement, changelog, and the excellent Ruby References website, which covers all the new features in detail with lots of examples.

Nithin Bekal
Hi, I’m Nithin! This is my blog about programming. Ruby is my programming language of choice and the topic of most of my articles here, but I occasionally also write about Elixir, and sometimes about the books I read. I'm @nithinbekal on Twitter.