Source: unsplash.com

The yield Keyword in Ruby

Tech - RubyCademy
RubyCademy

--

In this article, we’re going to explore the following topics:

  • the yield keyword
  • blocks & yield arguments
  • return values
  • the Enumerable#map method

The yield keyword — in association with a block — allows to pass a set of additional instructions during a method invocation.

This mechanism allows you to customise a method depending on your needs.

What’s a block ?

A block is part of the Ruby method syntax.

This means that when a block is recognised by the Ruby parser then it’ll be associated to the invoked method and literally replaces yields in the method

produces

one yield
multiple yields
multiple yields

The code inside the block will “replace” yield keyword in the method definition.

Now, what happens if we don’t pass a block during method invocation for a method that includes yield?

block_given?

When yield is called in a method, the method requires a block. Otherwise, a LocalJumpError is raised

So, how to make the block optional ? By using Kernel#block_given?

Here, yield is only processed if a block is passed during a method call.

Arguments

yield can take a list of arguments that will be available for the block

Here, the block takes |hello, world| as arguments. Those arguments are provided by yield inside yield_with_arguments.

Return value

It’s possible to get the return value of a block by simply assigning the return value of a yield to a variable

hello_world contains "Hello World!" returned by the block.

Array#my_map

Now that we’ve made an overview of yield, let’s try to recap the important notions by implementing an example.

Enumerable#map allows you to iterate over a list of objects and manipulate each of them. Then it returns a new list that contains all the manipulated objects.

$> array = [1, 2, 3]
=> [1, 2, 3]
$> array.map { |n| n + 2 }
=> [3, 4, 5]
$> array
=> [1, 2, 3]

In the above example, we assign [1, 2, 3] to array.

Then we call array.map { |n| n + 2 } that adds 2 to each element of array. A new array that contains [3, 4, 5] is returned by array.map.

Finally, we see that the initial array didn’t change.

Now, let’s try to implement our own version of Enumerable#map directly on the Array class

$> array = [1, 2, 3]
=> [1, 2, 3]
$> array.my_map {|n| n + 2}
=> [3, 4, 5]
$> array
=> [1, 2, 3]

Our custom method has the same behavior as the Enumerable#map method.

That’s great! Now let’s dive into the code.

1- we create a temporary array named ary. This variable will contain the return value of our method.

2- we iterate over the calling array by using self.each.

3- for each element, we call yield(elem) — where elem is the current element of the iteration. Then, we store the return value of yield(elem) in ary variable.

4- we return ary.

Now, what happens if we forget to pass a block to our Array#my_map method?

$> array.my_map
LocalJumpError (no block given (yield))

As you probably expected, an error is raised.

Fortunately, we’ve already fixed this issue in the previous sections.

So let’s reuse the Kernel#block_given? method to enhance our Array#my_map method

$> array.my_map
=> [1, 2, 3]

If there is no block passed to the method then we force the method to return a copy of the calling array.

Ruby Mastery

We’re currently finalizing our first online course: Ruby Mastery.

Join the list for an exclusive release alert! 🔔

🔗 Ruby Mastery by RubyCademy

Also, you can follow us on x.com as we’re very active on this platform. Indeed, we post elaborate code examples every day.

💚

--

--