Skip to content

Instantly share code, notes, and snippets.

@mzagaja
Created December 12, 2022 18:37
Show Gist options
  • Save mzagaja/9910fb4d1c16fd617dac5a7e7bb4ba1a to your computer and use it in GitHub Desktop.
Save mzagaja/9910fb4d1c16fd617dac5a7e7bb4ba1a to your computer and use it in GitHub Desktop.

The other day we had an issue with scope when migrating from the Ruby 2 to Ruby 3 syntax for hash arguments. Previously we had something like:

def foo(**kwargs)
  kwargs[:value] += 1
end

In Ruby 2 you can then do:

hash = { value: 1 }
foo(hash)

and the value of kwargs[:value] inside the method would increment, but it would not impact the value outside the method.

In Ruby 3 foo(hash) is now an error:

(irb):1:in `foo': wrong number of arguments (given 1, expected 0) (ArgumentError)

To fix this the migration guide suggests you might change the method to read:

def foo(kwargs = {})
  kwargs[:value] += 1
end

This is mostly equivalent but the trick is this now mutates the value of kwargs[:value] outside the method. Suddenly you might see bugs you did not see before.

What This Means

In Ruby 2 def foo(**kwargs) creates a copy of the hash and changes it inside the method without the impacting its value outside the method. In Ruby 2 and 3 the def foo(kwargs = {}) syntax merely passes the hash into the method and any changes you make to it are seen inside or outside the method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment