The defined? keyword in Ruby
An overview of the defined? keyword with a use case from the Ruby source code.. and an optimization tip!
In this article, we’re going to explore the following topics:
- the
defined?
keyword defined?
in the context ofresolv-replace
defined? yield
vsblock_given?
The defined? keyword
In Ruby, the defined?
keyword returns a string that represents the type of entity passed as an argument. Here is an exhaustive list of what you can pass to defined?
Here, we can see that defined?
handles a maximum of cases. But some cases are more relevant than others. For example, let’s see how the resolv-replace
library — available in the Ruby Standard Library — takes advantage of this keyword to handle a really “tricky” case.
defined? in the context of resolv-replace
This library simply monkey-patches (IP|TCP|UDP|SOCKS)Socket
classes provided by the socket
library to use the Resolv
DNS resolver.
In the case of SOCKSSocket
, the monkey-patching only intervenes when the Ruby interpreter is compiled with the --enable-socks
flag. So let’s see how the resolv-replace
library handles this tricky case
Notice the modifier-if
line 10. Indeed, to choose whether to monkey-patch this class or not, the resolv-replace
library simply checks if the SOCKSSocket
constant is defined using the defined?
keyword. Indeed, as seen in the previous section, SOCKSSocket
is only defined if the Ruby interpreter is compiled using the --enable-flag
.
So, as the class
keyword is only syntactic sugar for class definition and class opening, then you can call a modifier-if to execute this code (or not).
Now let’s see how using defined?
can positively impact the performance of your application.
defined?(yield) vs block_given?
If in your program you deal with a huge amount of blocks, then defined? yield
can definitely become a real optimization for your program performance. Indeed, to check if a block is passed to your method, you normally use the Kernel#block_given?
method
Here method_with_block_given
checks if a block is passed as an argument by using Kernel#block_given?
. If so, then yield
is called and the block is executed. Otherwise, block_given?
returns false
and yield
is never executed.
Ruby provides another way to achieve the same result: defined?(yield)
The main difference between these 2 expressions is that defined?(yield)
is faster than Kernel#block_given?
. Indeed, it’s a keyword when the block_given?
is a method. So block_given?
is slower because of the cost of the method call added to the cost of the Method Lookup Path.
Let’s generate benchmark-ips
reports to see what’s the difference between defined?(yield)
and block_given?
We can see that block_given?
is 1.27x slower than defined?(yield)
.
But as seen in the above benchmark, we’re able to call block_given
9.897M times in a second. So keep in mind that this optimization is only relevant on a huge amount of block calls.
Conclusion
defined?
isn’t the most popular keyword in Ruby. Nevertheless, in some situations, this keyword becomes a powerful asset to enhance your program performance or to handle tricky cases where a constant, method, etc... is conditionally loaded. Please feel free to share with us your experience with this keyword in the comments section.
Ruby Mastery
We’re currently finalizing our first online course: Ruby Mastery.
Join the list for an exclusive release alert! 🔔
Also, you can follow us on x.com as we’re very active on this platform. Indeed, we post elaborate code examples every day.
💚