Exploring Ruby Splat Operators

A look at the Single and Double splat operator

Ignacio Chiazzo
Better Programming

--

Developers tempt to use the splat operator as soon as they learn its usage. The operator is great and works in many cases, but we have to be aware of its power and consequences.

In this post, I will describe what the Ruby Single and Double splat operators are, along with dangerous usages I have seen in production. The purpose is to show the operator's power to use it confidently and identify dangerous cases during code review.

Splat operator

There are two types of Splat operators: single (*) and double (**). Both operators have two main functions: Construct and Spread out.

The Single Splat operator (*) constructs and spreads out Arrays. Let's see some examples:

In the previous example, we can see that the single splat operator grabs the elements of the prime Array and spreads them out. It's also helpful to grab items within an array and assign them to another variable, as the rest variable shows in the previous example.

We can also insert array elements into another Array. In the example below, primes elements are added to the numbers Array, starting from the position where *primes is called.

We can also use it to construct an array from a single element. Adding the Splat to the value will convert the value to a new Array. See the following example:

The double Splat operator was released on the 2.0 Ruby version, and it works similarly. We can construct and spread out Hashes. See the following example:

These operators can be used in functions. Either in the function's definition (as argument) or when calling the function as an argument value. To dive into these two cases, let's first see which types of arguments a Ruby function can have.

Types of arguments in Ruby

A function can have different types of arguments:

  • Positional arguments.
  • Keyword arguments (also known as kwargs).
  • Splat arguments (Single and Double operator)

The positional arguments are the simplest ones. Keyword arguments have a key (a symbol). An example describes a thousand words:

arg_1 and arg_2 are positional arguments, while key_1 and key_2 are kwargs.

These two types of arguments can have default values, meaning that the caller doesn't need to pass them.

Single Splat operator in a function

Single Splat operator when calling a function

The splat operator allows us to pass the elements of an array to the function.

In this example, calling *arr spreads out the array arr elements and passes its values to the function as positional arguments in the order it was given.

Note that if the array arr contains more values than the positional arguments (2), it will error (try passing arr = ["1", "2", "3"] ). Similarly, if you pass fewer arguments, you will get an error. The number of arguments passed must match the number of positional arguments accepted by the function (considering the arguments with a default value).

Single Splat operator as an argument in the function's definition

We have seen how we can use the Splat operator as a value, but we can also use it as an argument in the function definition. See the following example:

arg_1 is an argument that uses the splat operator, meaning that the positional arguments are the elements of the array arg_1 . Hence, you can pass 0 or more positional arguments to the function.

It doesn't necessarily need to be the last argument. It can be in the middle of the function as the following example shows.

Double Splat operator in a function

This operator is very similar to the Single operator, but it's for Hashes (and kwargs).

Double Splat operator when calling a function

Two important things to keep in mind:

  • The Hash must have precisely the same keys.
  • The Hash must have the keys as symbols, not strings. Read this issue if you are curious to see why it doesn't use Strings.

Double Splat operator as an argument in the function's definition:

The function accepts 0 or more kwargs that are part of the hash. A difference with the Single Splat operator that can be in any position, the Double splat argument must be the last in the argument list.

You might have wondered what's the difference between using the Splat ** Vs just a Hash variable in a function definition? Well, there are a few differences:

  • The Double Splat is optional. You don't need to pass a value to the function.
  • The Double Splat cannot have a default value, while the simple Hash can. The default value of the Double Splat is implicitly the empty Hash ({} ).
  • If there are default values in positional arguments, the results could differ. See the following example:

Combining different types of arguments

You can combine single and double Splat operators as long as the Double Splat is the last argument and the number of arguments required is satisfied. See this example:

Be careful!

I've seen different bugs in production related to this operator, and all of them have mostly the exact cause: The sizes of arguments passed are different than the size required. This affects double and single operators.

Let's see an example. Take some time to read the following piece of code, and think about why this code is terrible before reading the reason below.

Imagine that we have an ActiveRecord class Post linked to the table posts containing three columns id , title and description .

The class PostSchema is a wrapper of the Post class. It has a constructor that accepts the exact three attributes that the's table has.

The Service has a method that receives a post_id and looks up the table to find the post. If a post is found, it gets its attributes as a symbolized Hash and assigns them to the attributes variable. Then it creates a PostSchema using the double Splat with the attributes variable. Finally, we print out the PostSchema.

Have you noticed anything wrong with this code? If not, take some time to read the print_post function.

What happens if I change the schema of the posts table? For example, let's add a new column created_at to the posts table:

๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ Boom! The test now fails! The reason is that the Splat operator now passes a new argument created_at that PostSchema 'constructor doesn't support.

This is a corner case, but definitely cases like this happen! Other bugs might be caused by different factors, such as using the double Splat with the keys as strings, but I haven't seen many.

Conclusion

The splat operator is beneficial, and I suggest using it. However, you should be careful when using it within functions. Mainly, make sure that the function satisfies the size of the arguments passed. Additionally, pay extra attention when there are default values, and ensure the keys are symbols if you use the double Splat as an argument value.

--

--