How is symbol passing as block implemented in Ruby
Recently, one of the intern at my company asked me how can we pass a symbol as a block parameter to a method call in Ruby. Let’s take a look at how it is implemented in Ruby.
First thing first, a small script to help us decompile Ruby code to Ruby’s bytecode instructions.
To use this script, we need to provide the file name of a Ruby script. For example:
$> echo puts "Disasm this" > test.rb $> disasm test.rb == disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>========== 0000 trace 1 ( 14) 0002 putself 0003 putstring "Disasm this" 0005 opt_send_without_block <callinfo!mid:puts, argc:1, FCALL|ARGS_SIMPLE> 0007 leave
having enough tool, let’s dig into the code.
Our concern is the following code:
Let take a look at the compiled bytecode
== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>========== 0000 trace 1 ( 1) 0002 duparray [1, 2, -3] 0004 putobject :abs 0006 send <callinfo!mid:map, argc:0, ARGS_BLOCKARG> 0008 leave
for anyone who is not familiar with these bytecodes, the above code will:
[1,2,-3]to stack(as a receiver of method
- push symbol
:absto stack(as a block argument)
One interesting thing here is the flag
ARGS_BLOCKARG, which hold true when passing
Let’s take a look at how
send instruction is implemented in Ruby(it is defined in insns.def of Ruby’s source code)
We can see that when
ARGS_BLOCKARG flag is set(which mean we are passing a block),
ci->flag will be set to
Let’s find the definition of
vm_caller_setup_arg_block(it is defined in
Wow, lots of stuff happens here, but what draw my attention is the condition
ci->flag & VM_CALL_ARGS_BLOCKARG which is
true in our case.
In the middle of the
if statements, we can see a condition to check if the passing block is a
Proc, if it is not, a calling to function
rb_check_convert_type(proc, T_DATA, "Proc", "to_proc") will be used.
If the passing block is not a Proc,
Symbol#to_proc will be used to convert out symbol to Proc before going on.
So, underneath, Ruby will convert my symbol to a
Proc and passing this as a block parameter to
This method will return a Proc object which will response to the given method by symbol.
Therefore when we declare such as
:abs will be converted to a Proc and then passing each of the element as a parameter to this