Current syntax

I have just started learning Rust and I thought it would be interesting for you to get some feedback where I struggle with (in terms of syntax design, which is the reason why I’m posting this here) and what I think of Rust so far. Don’t hesitate to delete this thread if this matter should be discussed somewhere else.

Here is a code example (taken from the tutorial) and some comments about it:

impl<T: PartialEq> PartialEq for List<T> {
    fn eq(&self, ys: &List<T>) -> bool {
        // Match on the next node in both lists.
        match (self, ys) {
            // If we have reached the end of both lists, they are equal.
            (&Nil, &Nil) => true,
            // If the current elements of both lists are equal, keep going.
            (&Cons(ref x, box ref next_xs), &Cons(ref y, box ref next_ys))
                    if x == y => next_xs == next_ys,
            // If the current elements are not equal, the lists are not equal.
            _ => false
        }
    }
}
  1. The for keyword in the implementation. Although it speaks for itself, it’s odd in that place because it’s different to the for keyword of a loop.
  2. I don’t understand &, *, ref and box (at the moment, at least). The combination of them makes it even worse. As I know C, I do understand where the ampersand and the asterisk are coming from and what they do. However, I also remember how much time it cost to understand pointers and derefencing.
  3. I’m questioning whether it is a good idea to assume that well-known operators (& and *) should be used because they are well-known. Maybe they are just bad habits we have established. The asterisk seems a bad idea to use for dereferencing because it is a unary and a binary operator at the same time. The same applies to the ampersand.
  4. The amount of magic symbols is a problem from my point of view: One has to understand each symbol before he or she is able to read the code. Again, a combination makes it even worse. This is the main reason why I prefer Python’s syntax over pretty much any other language’s syntax so far. Have a glimpse at this page. Just a glimpse of it will show you the difference. Rust looks more bloated with lots of symbols. Now, I don’t understand much of Haskell, but I can read the code a lot easier. The reason is simple: Less symbols.
  5. I guess it’s too much to ask, but I’d love to get rid of semicolons and curly brackets (for blocks). I love Python because the syntax is extremely simple. Yes, the amount of lines may be higher in some cases but it’s much more readable and easy to understand. What is so wrong with Pythons approach, forcing code writers to indent properly?

To cut a long story short: Can we trade a bit of expressiveness of C/C++ for some Python simplicity?

I will continue reading the tutorial and will post updates about my thoughts when they occurr.

Cheers, Lennart

1 Like

I really think you should give Rust more than a few hours before complaining about the syntax... many syntactial choices are arbitrary and will end offending someone. In particular, a big target audience of Rust are C/C++ programmers. Many changes you suggest would make the language more alien in their eyes. Rust tries very hard (to a fault at times, in my opinion) to emulate C/C++ syntax, which is the reason for its 'familiarity'. FWIW, I don't like Python's syntax and wish it used whitespace insensitive blocks.

There are two specific things I want to comment on:

The semicolon serves a very important purpose in Rust, please continue the tutorial/guide to find out what it is. It definitely pays its rent. I should note, however, that in the snippet you posted there isn't a single semicolon.

A lot of the appeal and, arguably, purpose of Rust is the great amount of control over memory/ownership it allows you to have. In particular, it allows you to write expressive, memory safe code without a garbage collector. The reason why Haskell and Python get away with fewer symbols is because they don't let you have that control and force you to use a garbage collector. To that end, interacting with pointers is somewhat common in Rust and therefore it should use short symbols for those operations.

1 Like

If you believe Haskell has fewer symbols than Rust, then I question how much Haskell you’ve been exposed to. Haskell allows basically arbitrary operator definitions, you’ll eventually run into libraries that think things like ‘<*>’ or ‘$$$’ are good names to use.

1 Like

That’s different from symbols defined by the parser like { and ; and so forth

Except that Haskell is actually a braces-and-semicolons language. The Haskell everyone writes in practice follows the ‘layout rules’, which allow them to be omitted, but they’re absolutely part of the syntax.

http://www.haskell.org/onlinereport/lexemes.html

that’s why I mentioned particularly those symbols since they ARE in the language while symbols like $ are a function in the standard library

I believe any special symbols can be used as infix functions like in Smalltalk

I absolutely dislike the fact that python forces indentation. I much prefer ruby’s way. It basically uses blocks that can be indented or not, as you wish. They end with the “end” keyword, which is (IMO) better than having lots of curly braces.

Ruby syntax is elegant and it would be amazing that a compiled, system language like rust would adopt it. I don’t see why we need to keep using curly braces, I don’t particularly like them. But I know it’s only a matter of taste and that it’s probably too late to make such big changes to rust’s syntax.

You bring a good point about semicolons, though. As other people told you, a lack of semicolon indicates that you want your branch to evaluate to the result of that expression. Do you know why we can’t have the

if (condition)
  expression;
else
  expression;

of C? Because we made the {}'s compulsory, so that we can write if statements without parentheses. What’s more useful, {}-less conditions or ()-less conditions? ()-less conditions were deemed more useful because conditions with multiple lines are more common. Yet look at what we’re doing with semicolons. Lines that “return” a value are much less common than lines that don’t, yet we use a semicolon to represent those that don’t.

Something like this:

let a = if (condition) {
  expression
  expression
  expression
  expression; // the branch will return this value
}

would make more sense to me. It means we would have to type the semicolon less often. Yet it’s still ugly. I think we should go with a keyword, like “value” for example:

let a = if (condition) {
  expression
  expression
  expression
  value expression // the branch will return this value
}

Not sure if rust developers have considered such a thing. I’m curious to hear about it.

1 Like

()-less conditions were deemed more useful because conditions with multiple lines are more common.

That’s not why Rust uses ()-less ifs. The main reason is because

if (foo)
    bar;
    baz;

looks like if (foo) { bar; baz; } but is actually if (foo) { bar; } baz;.

Lines that "return" a value are much less common than lines that don't

I’m not so sure about this. A lot of good Rust code is written in a highly functional style, meaning that semicolons are in fact quite rare. Not needing semicolons is also very useful in the common constructor function case; e.g., fn make_a_foo() -> Foo { Foo { x: 0, y: 0 } }. And in any case, the semicolon acts as a separator. Replacing that with line breaks would be the first part of Rust’s syntax that was dependent on line breaks (apart from string literals and line comments), or even dependent on whitespace in general (apart from separating identifiers/keywords).

1 Like

Can confirm this with personal experience and most of the Servo code I've looked at. The only time you need a semicolon is when an expr returns something which you don't want to do anything with. That's ... rare when you write code functionally.

Can confirm, only trait definitions and my main() has semicolons in it.

Thanks for all your responses. I've had a lot of exams to write, so I apologise for my late response.

Yes, I am new to Rust but think it's a good idea to display my thoughts from a Rust beginner's point of view. In my opinion, this is a chance you only get once per person because I will get used to the syntax of Rust as I got used to the syntax of C. However, getting used to something doesn't mean it's good.

I think it's highly questionable whether emulating the C/C++ syntax is a good idea. Is it a good idea to use * for dereferencing and & for an address? Maybe it is, but then Rust has a ref keyword which, in my eyes, clashes with the ampersand. It might be provocative to say that without much knowledge about the actual difference between them, but for me it doesn't make sense to use a single symbol for one way and a word for the other way. It seems inconsistent.

As sam113101 suggested, it could be a lot clearer by using a keyword called value in a block. I'd prefer that over a semicolon.

Please explain why you dislike forced indentation. Yes, there are problems with tabs and spaces, but that's pretty much it as far as I can see.

I really like that idea.

But what if we'd use the value keyword only for blocks with multiple statements? I guess, that would be inconsistent...

I don't want to dismiss the value keyword idea, yet. It needs some more thought, though.

As I've had the pleasure to do some lexing in the past, I know how painful whitespaces can be. But what would be the problem with line breaks? Of course, coders would have to use them instead of semicolons, which would force them to format properly (no offense to anyone).

I don’t really understand what people dislike so much about semicolons and brackets. Considering that most of Rust’s userbase will end up coming from C/C+±land, it makes a lot of sense to base the syntax off of that, and I frankly love the current approach.

Syntactically, I think Rust’s primary goals are being:

  1. Small, yet extendable through traits and syntax extensions.
  2. Approachable to C/C++ programmers.

I personally prefer the semicolon over value because it borrows from and builds on C/C++ syntax, whereas value is inspired more by Python and friends (plus I like having “value” as an available variable name). You can’t please all types of programmers out there, and frankly, Rust would be much better served by catering to the C/C++ crowd than to the Python crowd.

Personally I think Rust's use of semicolons is really innovative and useful. It alters the meaning of an expression such that it evaluates to (). This has real-life syntactic relevance, instead of being a ceremony that you perform after typing each line of code.

Further, coming onto a new programming language's forum and arguing "making a new language that uses braces and semicolons for syntax is outdated" seems silly. Especially when the language is taking these old ideas and putting a new spin on them.

It's hard to argue things like

  • syntax-relevant whitespace is a good thing
  • braces and semicolons are good things
  • Ruby end blocks are better than syntax-relevant whitespace in Python's blocks

because they're all just a matter of the original language creators' taste. Nobody can form a consensus on those things, and changing them after they're part of a language is hard because the people who know the language are already used to its particular take on the matter.

I write Python and Rust fairly regularly. I write C and Haskell sometimes. They all have interesting takes on these things, but I can't come up with a good way to argue against any particular choice - it's more "the way things are" than "something good/bad about a language".

The ref keyword is valid only within match patterns. I hope this example helps you get the idea.

Btw I think he made a really good point here. By symmetry argument, ref should be replaced with *.

@nodakai

The principle of pattern matching is, patterns take the same form of “constructors”, and *foo is not an expression that constructs something, so it makes no sense for * to be appearing in patterns.

While ref is specially designed to do a referercing binding, even if the value is not constructed by a * (which is not possible anyway), so ref and “reverse-*” is not the same thing.

EDIT: removed bits that is actually agreed between the two of us. Sorry for previously stating the obvious.

On second thought, this can be a good idea, but maybe there are corner cases?

I really wish know the “magin behind semicolon”…I’ve read a few post about it and I can’t understand: I’ve used scala without semicolons for 4 years and I haven’t had any of the problems than people mention about not semicolon languages…

don’t missunderstand me…I don’t care if rust uses semicolon or braces, a language is much more than its syntax and rust has many interesting features missing in other systm languages…

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.