do..end vs curly braces for blocks in Ruby
Solution 1
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.
Solution 2
From Programming Ruby:
Braces have a high precedence; do has a low precedence. If the method invocation has parameters that are not enclosed in parentheses, the brace form of a block will bind to the last parameter, not to the overall invocation. The do form will bind to the invocation.
So the code
f param {do_something()}
Binds the block to the param
variable while the code
f param do do_something() end
Binds the block to the function f
.
However this is a non-issue if you enclose function arguments in parenthesis.
Solution 3
There a few points of view on this, it's really a matter of personal preference. Many rubyists take the approach you do. However, two other styles that are common is to always use one or the other, or to use {}
for blocks that return values, and do ... end
for blocks that are executed for side effects.
Solution 4
There is one major benefit to curly braces - many editors have a MUCH easier time of matching them, making certain types of debugging much easier. Meanwhile, the keyword "do...end" is quite a bit harder to match, especially since "end"s also match "if"s.
Solution 5
The most common rule I've seen (most recently in Eloquent Ruby) is:
- If it's a multi-line block, use do/end
- If it's a single line block, use {}
Related videos on Youtube
Blake Taylor
I love Rails and develop with it at AgLocal.com. As of late I've also been paying attention to Coffeescript, Backbone, Node, Neo4j and Clojure. I get excited about interesting and effective processes, having my computer do things for me, writing programs about programs, and the design of all good things.
Updated on February 26, 2022Comments
-
Blake Taylor about 2 years
I have a coworker who is actively trying to convince me that I should not use do..end and instead use curly braces for defining multiline blocks in Ruby.
I'm firmly in the camp of only using curly braces for short one-liners and do..end for everything else. But I thought I would reach out to the greater community to get some resolution.
So which is it, and why? (Example of some shoulda code)
context do setup { do_some_setup() } should "do somthing" do # some more code... end end
or
context { setup { do_some_setup() } should("do somthing") { # some more code... } }
Personally, just looking at the above answers the question for me, but I wanted to open this up to the greater community.
-
Daniel Lee about 13 yearsJust wondering but what are your coworkers' arguments for using braces? Seems more like a personal preference than a logic thing.
-
halfdan about 13 yearsIf you want a discussion make it a community wiki. To your question: It's just personal style. I prefer the curly braces as they look more nerdy.
-
PJP about 13 yearsIt isn't a preference thing, there are syntactic reasons for using one over the other in addition to stylistic reasons. Several of the answers explain why. Failing to use the right one can cause very subtle bugs that are hard to find if another "stylistic" choice is made to never use wrapping parenthesis for methods.
-
Blake Taylor about 13 yearsWow, some great answers, I didn't expect so much. For my own sake, I want to point out I wasn't oblivious to the precedence difference. I just believe an edge condition that would only effect the outcome of 1% of my code shouldn't have that great of an effect on how I style the other 99%. When and if it mattered, I'd make exception from whatever style detected.
-
PJP about 13 years"Edge conditions" have a bad habit of sneaking up on people who don't know about them. Coding defensively means a lot of things, including deciding to use coding styles that minimize the chance of ever running into the cases. YOU might be aware, but the guy two people after you might not be after the tribal knowledge has been forgotten. It tends to happen in corporate environments unfortunately.
-
Blake Taylor about 13 years@tinman These types of edge conditions are handled by TDD. This is why Rubist can write code like this and get a full nights rest knowing such errors don't exists in their code. When developing with TDD or BDD and such a mistake in precedence is made the red shown on the screen is what reminds us of the "tribal knowledge". Usually the solution is to add a couple of parens somewhere which is totally acceptable within the standard conventions. :)
-
awendt about 11 years@BlakeTaylor Sorry, but saying that these cases are handled by TDD is just not true. I just got bitten by this in a test and spent half an hour debugging it :(
-
Cees Timmerman over 3 yearsDoes this answer your question? Using do block vs braces {}
-
-
PJP about 13 yearsIt is more than preference. The binding between the two is different and can cause problems that are hard to diagnose. Several of the answers detail the problem.
-
PJP about 13 yearsIt isn't a matter of personal preference only. Binding of parameters can cause subtle bugs if method params aren't enclosed in parenthesis.
-
Kelvin almost 13 yearsIn terms of people of other language backgrounds - I think the difference in "understandability" is so small in this case as to not warrant consideration. (That wasn't my downvote btw.)
-
ck3g over 12 yearsRubyMine for example, highlight
do ... end
keywords by default -
ben over 9 yearsWhy does
puts [1,2,3].map do |k| k+1; end
only print the enumerator i.e. one thing. Isn't puts passed two arguments here namely[1,2,3].map
anddo |k| k+1; end
? -
bonyiii over 8 yearsgithub.com/bbatsov/rubocop/issues/1520 f = lambda do |param| ... end scope :some_scope, f Maybe this looks unidiomatic, and then there's the other alternative, which I thinks is ugly: scope :some_scope, (lambda do |param| ... end)
-
Chinoto Vokro about 8 yearsWhile I do prefer braces, I see no reason that an editor would have trouble finding a 2/3 character string versus a single character. Though there is the argument that most editors already match braces, whereas
do ... end
is a special case which requires language specific logic. -
Kelvin about 8 yearsStrictly speaking, there is another "if" syntax - the postfix "if" (
do_stuff if x==1
), which is kind of annoying to convert to the regular syntax. But that's another discussion. -
Eido95 almost 8 years
[1,2,3].map do |k| puts k+1 end
seems more readable thenputs [1,2,3].map { |k| k+1 }
-
karatedog over 7 years@ben: exactly, because it is
puts
that gets two parameters. First.map
gets evaluated, but it did not get a block (as the thing that looks like a block is not.map
's argument), so it returns an enumerator which becomesputs
first argument. Andputs
will get that block as 2nd argument, butputs
does not use any passed block, so there happens nothing. Parentheses fix this problem:puts ([1,2,3].map do |k| k+1; end)
-
Mecki about 5 yearsThere are prefix
if
, postfixif
, postfixunless
(also a kind ofif
, a if-not) and one lineif
withthen
. The Ruby way is to have plenty of ways to express in fact something that is always the same thing, much worse than what C is offering that follows very simple, strict rules.