Using do block vs braces {}
Ruby cookbook says bracket syntax has higher precedence order than do..end
Keep in mind that the bracket syntax
has a higher precedence than the
do..end syntax. Consider the following
two snippets of code:
1.upto 3 do |x|
puts x
end
1.upto 3 { |x| puts x }
# SyntaxError: compile error
Second example only works when parentheses is used, 1.upto(3) { |x| puts x }
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.
Ruby do/end vs braces
That's because the second line is interpreted as:
p(a.map) do ... end
instead of:
p(a.map do ... end)
The grammar is ambiguous in this case and do
doesn't seem to bind as strongly as {
.
differing `puts` behavior inside `do..end` blocks and curly braces in Ruby
do...end
and {}
are 100% semantically equivalent for method blocks. Their only difference is their parsing precedence, so they evaluate differently
To really understand that difference, a few things first.
Ruby lets you call methods without parens:
my_object.my_method my_arg
# so my_arg could actually be a method! Let's put parens in to show that:
my_object.my_method(my_arg())
Blocks in Ruby are method arguments -- a syntax for passing in closures (except for the special keywords that act in the parent scope). The below two chunks are equivalent:
[1, 2, 3].map { |x| 2 * x }
# split out into two lines
double = ->(x) { 2 * x } # shorthand for `lambda { |x| 2 * x }`
[1, 2, 3].map(&double)
Okay, so knowing all that, let's expose the difference between {}
and do...end
:
my_method [1, 2, 3].map { |x| 2 * x }
my_method([1, 2, 3].map { |x| 2 * x }) # parses like this
my_method [1, 2, 3].map do |x| 2 * x end
my_method([1, 2, 3].map) do |x| 2 * x end # parses like this
my_method([1, 2, 3].map) { |x| 2 * x } # in other words
{}
has more precedence than do...end
, immediately getting associated to the method immediately to its left. do...end
has lower precedence, and will associate with my_method
, which gets passed [1, 2, 3].map
and the block as arguments.
This means, what you did above is:
puts(my_array.each) { |num| num *= 2; puts "The new number is #{num}." }
You've passed into puts
the my_array.each
, which is an enumerator, and a block, and puts
does nothing with the blocks passed into it, as do all methods by default.
Does Ruby read do and end the same way as { and }?
No, it doesn't. As a general rule of Ruby, if two things look alike, you can bet that there is a subtle difference between them, which makes each of them unique and necessary.
{
and }
do not always stand in the role of block delimiters. When they do not stand in the role of block delimiters (such as when constructing hashes, { a: 1, b: 2 }
), they cannot be replaced by do
... end
. But when the curly braces do delimit a block, they can almost always be replaced by do
... end
. But beware, because sometimes this can change the meaning of your statement. This is because {
... }
have higher precedence, they bind tighter than do
... end
:
puts [ 1, 2, 3 ].map { |e| e + 1 } # { ... } bind with #map method
2
3
4
#=> nil
puts [ 1, 2, 3 ].map do |e| e + 1 end # do ... end bind with #puts method
#<Enumerator:0x0000010a06d140>
#=> nil
As for the opposite situation, do
... end
in the role of block delimiters cannot always be replaced by curly braces.
[ 1, 2, 3 ].each_with_object [] do |e, obj| obj << e.to_s end # is possible
[ 1, 2, 3 ].each_with_object [] { |e, obj| obj << e.to_s end # invalid syntax
In this case, you would have to parenthesize the ordered argument:
[ 1, 2, 3 ].each_with_object( [] ) { |e, obj| obj << e.to_s } # valid with ( )
The consequence of these syntactic rules is that you can write this:
[ 1, 2, 3 ].each_with_object [ nil ].map { 42 } do |e, o| o << e end
#=> [ 42, 1, 2, 3 ]
And the above also demonstrates the case where {}
are not replaceable by do
/end
:
[ 1, 2, 3 ].each_with_object [ nil ].map do 42 end do |e, o| o << e end
#=> SyntaxError
Lambda syntax with ->
differs slightly in that it equally accepts both:
foo = -> x do x + 42 end # valid
foo = -> x { x + 42 } # also valid
Furthermore, do
and end
themselves do not always delimit a block. In particular, this
for x in [ 1, 2, 3 ] do
puts x
end
and this
x = 3
while x > 0 do
x -= 1
end
and this
x = 3
until x == 0 do
x -= 1
end
superficially contains do
... end
keywords, but there is no real block between them. The code inside them does not introduce a new scope, it is just syntax used to repeat a few statements. Curly braces cannot be used here, and the syntax could be written without do
, such as:
for x in [ 1, 2, 3 ]
puts x
end
# same for while and until
For the cases where {
...}
and do
...end
delimiters are interchangeable, the choice which ones to use is a matter of programming style, and is extensively discussed in another question (from whose accepted answer I took one of my examples here). My personal preference in such case is to emphasize readability over rigid rules.
Does using curly braces go against the Ruby way?
While some people go with "braces for one-liners, do-end for multi-liners", I personally find the following rule the most logical:
- use
do-end
when your block has side-effects (typically, witheach
and related methods) and - use braces when your block is without side-effects (
map
,inject
and alike)
This logic goes well with method chaining issue that Matt wrote about.
One benefit of this approach is that it is going to make you think about side-effects every time you write a block, and they are very important, although sometimes overlooked by coders with no functional programming background.
Another way to put it, without involving side-effects terminology would be:
- use
do-end
for blocks that perform - use
{
and}
for blocks that return
Here are couple of articles with more info:
http://onestepback.org/index.cgi/Tech/Ruby/BraceVsDoEnd.rdoc
http://talklikeaduck.denhaven2.com/2007/10/02/ruby-blocks-do-or-brace
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.
Related Topics
How to Call Shell Commands from Ruby
How to Find Where a Method Is Defined At Runtime
What Does the "===" Operator Do in Ruby
What's the Difference Between Uri.Escape and Cgi.Escape
Ruby: Kind_Of? Vs. Instance_Of? Vs. Is_A
When to Use 'Self.Foo' Instead of 'Foo' in Ruby Methods
Access Variables Programmatically by Name in Ruby
Heroku Upload-Precompiling Assets Failed
Ruby 2.0.0P0 Irb Warning: "Dl Is Deprecated, Please Use Fiddle"
What Exactly Is the Singleton Class in Ruby
How to Specify a Local Gem in My Gemfile
Methods in Ruby: Objects or Not
Find Indices of Elements That Match a Given Condition
Cannot Load Such File - Script/Rails: Getting This Error While Remote Debugging Through Rubymine