Optional curly brackets

A lot of programming languages include a feature called optional curly brackets where you can leave out the brackets if your statement is just one line of code.

Example in java:

if(doHelloWorld) System.out.println("Hello world!");

It would be cool too if you could remove the semicolon to make it directly return.

Example:

if mycondition "something"
// This would be the same as
if mycondition {
  return "something";
}

Being able to leave out these brackets result into the ability to write cleaner code faster, and almost everyone has it so i dont get why rust still doesnt.

Note that Java has required parens, as you just used there, because it has optional braces.

Rust makes the opposite choice, and has optional (even discouraged) parens around the condition, but required braces around the block:

if doHelloWord { println!("Hello world!"); }

Given that everywhere I've worked in C-like languages has ended up having a coding standard requiring braces even through they're optional in the language, I think Rust makes the better trade-off here.

60 Likes

You could probably remove both brackets completly tho.

Trying to remove all separation is not fruitful. You'll notice that even in Python, it's

if doHelloWorld:
    print('Hello world\n')

with a colon that's not strictly necessary, because people find it easier to read that way.

(And removing everything also opens up ambiguities like if (a - b) vs if a { -b }. Or if (a & b) vs if a { &b }. Or ...)

9 Likes

Another example:

if foo[0] [0][0] else bar

vs

if foo[0][0] [0] else bar

I find LL(1) grammars easiest to mentally parse, i.e. it's nice when the next token implies what syntax it is part of, and I think Rust is quite close to that (but not fully that).

13 Likes

Putting aside whether your proposal is doable, I would find this pretty surprising. This code in java:

if(doHelloWorld) System.out.println("Hello world!");

is not equal to this one:

if(doHelloWorld) {
    System.out.println("Hello world!");
}

While you may see this as harder to read, I find it easier to read. I came from C/C++ where this is allowed, and it sucked reading other people's code, especially when they haven't formatted their code correectly. When I used code pretty-printers, they did two things; put brackets around everything (including one-liners) and more importantly indented the code. In effect, everything looked like Python with brackets.

Rust doesn't rely on whitespace the way Python does, so unless you format everything you won't get that nice indentation. The issue is that while you can (and should) use rustfmt to clean up your code, if a project has different formatting standards than you're used to, then your code might not be indented nicely.

5 Likes

Even if it was possible (and it is not, as others said here), it would open the door to bugs like Apple's goto fail;. I don't want Rust to do the same mistake.

31 Likes

I think you either have curly brackets on the statement, or some delimiter (C uses parentheses) on the condition.

If you omit curly brackets on the statement, then you have a shift-reduce conflict when you try to parse if-else-if chains.

If you omit the parentheses but mandates curly brackets, you're always unambiguous.

2 Likes

+1, I was just going to mention goto fail.

@KekOnTheWorld, in brief, we shouldn't actively steer the language towards a direction which is proven to be extremely error-prone by decades of industrial experience. It's a flat no.

16 Likes

Mandatory curly braces are an intentional design choice. Optional braces don't provide the ability to write cleaner code, quite the opposite. They are a common source of mistakes, like dangling else or bad block nesting.

It's part of the features that Graydon Hoare (the original creator of Rust) is very proud that Rust shipped without:

https://graydon2.dreamwidth.org/218040.html

31 Likes

I feel like the current syntax has alot of room for improvement. I love gaurd clauses so this is a big gripe of mine! There are two pretty obvious solutions which could be implimented in parallel to the existing syntax. I know nothing about the inner workings of the parser so I have no idea if they would be feasible to impliment.

Most generically, fat arrows could be used. We already use them in match statments for this exact purpose.

// Current syntax
if foo || bar { 
    DoSomething() 
}

// Alternative
if foo || bar => DoSomething()  

For gaurd causes, the 'return', 'break' or 'continue' keyword could be used as a seperator. I appreciate it looks a little clastrophobic but it still prefectly readable esspecially after syntax highlighing.

// Current syntax
if foo || bar { 
    return Some(number) 
}

// Alternative
if foo || bar return Some(number)   

It's not hard to make a parser accept every conceivable string as valid. Insanity inducing maybe. For programming languages parsers typically accept a superset of the language and guess at corrections so as to make better error messages.

But anyway these extra symbols aren't just noise, they provide context to human readers and the compiler. Accepting if foo || bar return Some(number) means that this code:

if foo || bar
    do_one_thing();
    do_a_second_thing();

runs just fine! Did I forget a {} pair? Nobody knows. The edit distance between the code meaning one thing and the code meaning another thing becomes very small, and it is harder to guess at the intent and/or correct it.

if foo || bar => DoSomething() kind of makes sense, but in a match the bit to the left of the => is a pattern, not a boolean expression.

12 Likes

Very informative response, thanks toc.

In regard to your first example, there should ever be a world where that should be acceptable, it's impossible to evaluate with any degree of certainty. My point was, the return, break and continue keywords are never part of a logical expression, so there is no need to define a block to separate them.

I agree with the point about the fat arrow, conflating pattern matching with boolean expressions.

Note that

x > 0 || return -1;

is perfectly legal Rust code, which seems like "a logical expresssion" to me.

(Which does mean that, if you really wanted to, you could one-liner your guard clause example with (foo || bar) && return Some(number);.)

10 Likes

Thanks for taking the time to clarify and correct my misconception. That's a perfectly succinct solution.

Rust is an expression language. Most constructs are expressions. Break, continue, and return are also expressions of type ! (ie. they diverge), which is immensely useful for eg. early returning inside pattern matches.

3 Likes

As one of the maintainers of the parser and resident invalid code recovery experts, my face contorted while reading this proposal and thinking about the fallout from it.

There's nothing stopping us from making the parser recover and provide structured suggestions when code like this is written. There's nothing stopping rust-analyzer from auto-applying the surrounding braces around the code when the confidence is high. But I would put my foot down (not that it could realistically stop any popular proposal) on complicating the grammar because it makes more code valid, which makes inferring intent when people write invalid code much harder. The optional braces for if statements in other languages have been the source of high profile bugs (heartbleed goto-fail comes to mind), and others in this thread have already explained some of the rationales behind not having this feature, but I wanted to also raise the error messaging and recovery point. It's the same reason that we can tell you "you forgot a ; here" with high confidence, but still make you write it: a language with automatic semicolon insertion comes with a whole host of caveats and edge-cases, whereas suggestions can be "fast-and-loose" on what they tell you ("you might have forgotten a ; here, but maybe you meant something else, change your code to make it easier for me, feeble computer, to understand").

30 Likes

I think you underestimate the outcome, here -- I doubt you'd have any trouble convincing someone from lang to put a blocking concern for such a thing.

3 Likes

I'm somewhat trusting that t-lang already thinks about these things and I wouldn't even need to raise my concerns in the first place. They've been sage shepherds so far, and see no reason they wouldn't continue to be, which means I don't need to raise my voice in despair. When I said "popularity" I guess I had an implicit "amongst t-lang" in there, that even I hadn't realized I had.

3 Likes