In Progress
Unit 1, Lesson 21
In Progress

Ruby 2.4 Destructuring Conditional

Video transcript & code

The other day I read a blog post about new features in Ruby 2.4, and I cam across one that made me say "yaaaay!". Then I read in the blog post that you probably shouldn't use it, and I said "nooooo!". So, what is this controversial new feature?

Well, let's start with stuff that already works in any version of Ruby.

As I think I've mentioned in previous episodes, I'm a fan of the idiom where a value is assigned to a variable and tested for truthiness in an if statement all at once.

If the value being tested will only be used within the conditional statement, this idiom neatly encapsulates the variable within a visual semantic unit.

if(magic_word = ENV["MAGIC_WORD"])
  puts "The magic word is #{magic_word}"
end
# >> The magic word is xyzzy

Notice that I've enclosed the assignment in parentheses , which is the convention I use to visually flag the fact that I meant to perform an assignment here, and not a comparison.

Now let's change focus something seemingly unrelated. We're going to talk about the netrc gem for a minute.

The netrc gem provides an interface to the .netrc file, which is a semi-standardized repository of logins and passwords on UNIX-like systems.

Let's lookup a login for moria in the local netrc file.

require "netrc"
netrc = Netrc.read
netrc["moria"]
# => #<struct Netrc::Entry login="gandalf", password="mellon">

The result is a object for .netrc entries.

But this object has a special feature: we can perform destructuring assignment to "splat" it into two different variables.

When we do this, the first variable gets the login, and the second variable gets the password.

require "netrc"
netrc = Netrc.read
login, password = netrc["moria"]
login                           # => "gandalf"
password                        # => "mellon"

Now, imagine we wanted to use this feature in an if statement. If a .netrc can be found, we use it to log in. If not, we show an error message.

require "netrc"
netrc = Netrc.read
if(login, password = netrc["moria"])
  puts "Entering the mines of Moria with login #{login} and password #{password}"
  # ...
else
  warn "No login found!"
end
-:8: multiple assignment in conditional

With Ruby 2.3 and below, this code won't even parse. We get a "multiple assignment in conditional" error.

Now let's switch to Ruby 2.4. As I'm recording this, 2.4 is still just in a preview release, so I have to switch to my trunk build.

$ chruby trunk
$ ruby -v
ruby 2.4.0dev (2016-07-29 trunk 55770) [x86_64-linux]

When we run the code under this version of Ruby, it works!

$ ruby doorsofdurin.rb
Entering the mines of Moria with login gandalf and password melon

If we change the code to look up the wrong host , the if statement does what we'd expect and falls through to the else clause.

require "netrc"
netrc = Netrc.read
if(login, password = netrc["dwarrowdelf"])
  puts "Entering the mines of Moria with login #{login} and password #{password}"
  # ...
else
  warn "No login found!"
end
$ ruby doorsofdurin2.rb
No login found!

In my opinion, this change to Ruby eliminates an annoying inconsistency, where only some legal assignments could be performed in a conditional. This code should also look vaguely familiar to you if you have any experience with languages that make use of pattern-matching assignments, such as Elixir or Go.

And that, in a nutshell, is why I'm excited about this new Ruby language feature. Happy hacking!

Responses