do..end vs curly braces for blocks in Ruby
The general convention is to use do..end for multi-line blocks and curly braces for single line blocks, but there is also a difference between the two that can be illustrated with this example:
puts [1,2,3].map{ |k| k+1 }
2
3
4
=> nil
puts [1,2,3].map do |k| k+1; end
#<Enumerator:0x0000010a06d140>
=> nil
This means that {} has a higher precedence than do..end, so keep that in mind when deciding what you want to use.
One more example to keep in mind while you develop your preferences.
The following code:
task :rake => pre_rake_task do
something
end
really means:
task(:rake => pre_rake_task){ something }
And this code:
task :rake => pre_rake_task {
something
}
really means:
task :rake => (pre_rake_task { something })
So to get the actual definition that you want, with curly braces, you must do:
task(:rake => pre_rake_task) {
something
}
Maybe using braces for parameters is something you want to do anyways, but if you don't it's probably best to use do..end in these cases to avoid this confusion.
Instance_eval does not work with do/end block, only with {}-blocks
It's because when you pass block with curly braces, it is passed to instance_eval
method. But if you pass it with do-end
, it's passed to puts
method, so instance_eval
doesn't get block and raises an error.
Block not called in Ruby
Add parentheses to puts
puts(m do
x = 2
y = 3
x * y
end)
The output is 6.
Your code is equivalent to
puts(m) do
x = 2
y = 3
x * y
end
Passing block into a method - Ruby
The difference between do .. end
and curly braces is that the curly braces bind to the rightmost expression, while do .. end
bind to the leftmost one. Observe the following examples:
def first(x=nil)
puts " first(#{x.inspect}): #{block_given? ? "GOT BLOCK" : "no block"}"
"f"
end
def second(x=nil)
puts " second(#{x.inspect}): #{block_given? ? "GOT BLOCK" : "no block"}"
"s"
end
first second do |x| :ok end # second(nil): no block
# first("s"): GOT BLOCK
first second {|x| :ok } # second(nil): GOT BLOCK
# first("s"): no block
In the first case, the block made with do..end
will be bound to the first function (leftmost). In the second case the block made with curly brackets will be bound to the second function (rightmost).
Usually it's good idea to use parentheses if you have two functions and a block - just for readability and to avoid mistakes.
It's very easy to accidentally pass a block to puts
method, just as in your question.
Why does Ruby evaluate these two expressions differently? The only difference is {...} and do...end
Precedence matters. {}
has a higher precedence over method call. do
-end
has a lower precedence.
pp [1, 2, 3].map { |r| r + 1 }
is parsed as pp ([1, 2, 3].map { |r| r + 1 })
, which is:
pp([1, 2, 3].map { |r| r + 1 })
pp [1, 2, 3].map do |r| r + 1 end
is parsed as (pp [1, 2, 3].map) do |r| r + 1 end
, which is:
pp([1, 2, 3].map, &->(r){ r + 1 })
In the latter case passing block to pp
is a NOOP
.
What is the difference or value of these block coding styles in Ruby?
The rails team and many other rubyists prefer to use curly braces for one line blocks and do...end
for multi-line ones.
The only functional difference between the two is that the precedence of a do...end
block is lower than that of a {...}
block.
Ruby multiline block without do end
There is a subtle difference between the two syntaxes. { }
are higher precedence than do ... end
. Thus, the following will pass bar
and a block to method foo
:
foo bar do ... end
while the following will pass a block to bar
, and the result of that to foo
:
foo bar { ... }
So your examples will act the same. However, if you left the parentheses off:
> 3.upto 9 {
puts "Hi"
}
SyntaxError: compile error
(irb):82: syntax error, unexpected '{', expecting $end
3.upto 9 {
^
from (irb):82
from :0
> 3.upto 9 do
puts "Hi"
end
Hi
Hi
Hi
Hi
Hi
Hi
Hi
=> 3
So, { }
are more likely to catch you up if you leave off parentheses in Ruby, which is fairly common; for this reason, and because Ruby conditionals and other control constructs all use end
as a delimiter, people usually use do ... end
for multi-line code blocks that come at the end of a statement.
However, { }
is frequently use in places where do ... end
would be cumbersome, for instance, if you are chaining several methods together which take blocks. This can allow you to write short, one line little blocks which can be used as part of a method chain.
> [1,2,3].sort{|x,y| y<=>x}.map{|x| x+1}
=> [4, 3, 2]
Here's an example to illustrate this difference:
def foo arg
if block_given?
puts "Block given to foo"
yield arg
else
puts "No block given to foo"
arg
end
end
def bar
if block_given?
puts "Block given to bar"
yield "Yielded from bar"
else
puts "No block given to bar"
end
"Returned from bar"
end
irb(main):077:0> foo bar { |arg| puts arg }
Block given to bar
Yielded from bar
No block given to foo
=> "Returned from bar"
irb(main):078:0> foo bar do |arg| puts arg end
No block given to bar
Block given to foo
Returned from bar
=> nil
Related Topics
How to Do Static Content in Rails
How to Get Filename Without Extension from File Path in Ruby
How to Run All Tests With Minitest
What Does Class_Eval ≪≪-"End_Eval", _File_, _Line_ Mean in Ruby
Why Are Methods in Ruby Documentation Preceded by a Hash Sign
How to Use Define_Method to Create Class Methods
Ruby Run Shell Command in a Specific Directory
Convert Unicode Codepoint to String Character in Ruby
To Use Self. or Not.. in Rails
Ruby/Rails - Change the Timezone of a Time, Without Changing the Value
Undefined Method 'Visit' When Using Rspec and Capybara in Rails
Ruby Gem For Finding Timezone of Location
Long Running Delayed_Job Jobs Stay Locked After a Restart on Heroku
How to Dynamically Call a Math Operator in Ruby