Swapping two variables with a, b = b, a
Question
In Ruby, what is the idiomatic way to swap the values of two variables in a single line?
The correct answer is
-
a, b = b, aCorrect -
temp = a; a = b; b = temp -
a.swap(b) -
a ^ b ^ a
Explanation
TL;DR
a, b = b, a. Multiple assignment evaluates the entire right-hand side first, bundling the values together, then assigns them to the left-hand targets. Both reads happen before either write, so the values cross over without a temporary variable.
Step by step
a = 1
b = 2
a, b = b, a
a # => 2
b # => 1
The whole expression evaluates to the array of right-hand values:
a = 1
b = 2
(a, b = b, a) # => [2, 1]
Under the hood
puts RubyVM::InstructionSequence.compile("a, b = b, a").disasm
Output on Ruby 4.0, with the == disasm header line trimmed:
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 2] a@0 [ 1] b@1
0000 getlocal_WC_0 b@1 ( 1)[Li]
0002 getlocal_WC_0 a@0
0004 newarray 2
0006 dup
0007 expandarray 2, 0
0010 setlocal_WC_0 a@0
0012 setlocal_WC_0 b@1
0014 leave
Both getlocal reads run before either setlocal write: the current values of b and a are collected into an array (newarray), duplicated so the expression has a return value (dup), then unpacked onto the targets (expandarray). This read-everything-then-write-everything ordering is what makes the swap safe.
Edge cases
More than two targets rotate the same way:
x, y, z = 1, 2, 3
x, y, z = z, x, y
[x, y, z] # => [3, 1, 2]
Any assignable target works, not just local variables:
arr = [1, 2]
arr[0], arr[1] = arr[1], arr[0]
arr # => [2, 1]
h = { a: 1, b: 2 }
h[:a], h[:b] = h[:b], h[:a]
h # => {a: 2, b: 1}
Why the other choices fail
temp = a; a = b; b = temp works, but it takes three statements and a throwaway variable; multiple assignment removes exactly that noise. a.swap(b) does not exist, and no method could implement it: a method cannot rebind its caller's local variables. The XOR trick needs its results assigned (a ^= b and friends); the bare expression a ^ b ^ a computes a value and changes nothing:
a = 1
b = 2
a ^ b ^ a # => 2
a # => 1
b # => 2
Share this quiz
Comments
No comments yet. Be the first.