Rustfmt: Spaces between parentheses for tuples


#1

[Rhetorical question:] Is this a function/method call or a Enum-tuple?

let pairs = RymParser::parse(Rule::rym, &source);

It’s a method call in this case, and maybe seasoned Rustaceans can spot that immediately, but the use of parentheses for tuples does tend to lead to being ‘blinded by brackets’. I’d like to suggest that spaces are always used between the insides of the tuple parentheses; i.e.

// if this were an enum
let pairs = RymParser::parse( Rule::rym, &source );

This would mimic the behaviour of struct instances somestruct { ... }.

This may have the undesirable effect of adding too many spaces where not desired (are Ok & Err tuples?)

Rule::int_number => match pair.as_str().parse::<u64>() {
    Err( e ) => Err( new_error(ErrorKind::ParseInt(e)) ),
    Ok( i ) => Ok( i ),
},

But then the lint could perhaps apply only to fully-qualified tuples (with the module::tuple or enum::tuple format)

You’ll have to forgive my ignorance if this is not possible / out of the question / already been discussed and turned down, I’m new to Rust.


#2

As I learned recently, “Yes”.

If RymParser::parse is an enum tuple variant or a tuple struct, this is actually a function call to a constructor function that returns the type instance.

It turns out that the ambiguity or overloading of () brackets is better handled in the compiler by making the type name resolve to a function than by trying to make the parser understand when the () are not a function based on the name.


#3

The thing that may surprise you is that even when this is an enum, it’s still a function call of the enum variant’s constructor. For example:

enum Foo {
    One(i32),
    Two(i32, i32),
}

fn main(){
    let _: () = Foo::Two;
}

I deliberately used the wrong type so rust will correct me:

error[E0308]: mismatched types
 --> src/main.rs:7:17
  |
7 |     let _: () = Foo::Two;
  |                 ^^^^^^^^ expected (), found fn item
  |
  = note: expected type `()`
             found type `fn(i32, i32) -> Foo {Foo::Two}`

It really is a function!

The same is true of struct tuples in general – their name refers to both the type and the constructor function.


#4

Ok, good, so it is a function, but I’d like to think of it as putting the values into a tuple much like a struct constructor. This would help with visually recognising tuples in the code since Rust code is quite heavy with punctuation, compared to say Python or Haskell.


#5

You can manually format your code this way if you want. Since you mention rustfmt in the title, this detail matters. rustfmt parses the same as the compiler, and the point is that it’s parsed as a function call, and the function provides the behaviour much later in the compilation pipeline. I’d be surprised if it can tell the difference for automated forrmatting.


#6

Yes, my concern from an implementation perspective is if it’s even possible to semantically tell the difference. Indeed, I wouldn’t be posting here if I wanted to just format my code how I wanted, but as I’m new to Rust, and having come from Go (and several other languages), I like to the have the auto-formatting ability to reduce the amount of tidying I have to do.


#7

Put “variant constructor function” aside.

This is the exact way I wrote function call.

This is the exact way I wrote the condition part in if/while/for statements in C/C++.

I prefer offering larger room for those who are grouped by parentheses and make them happy. Do you agree with me? :smiley:


#8

I’m bit of a polyglot, having learnt Perl6, Haskell, Python, Go (and probably some others) before choosing Rust and I have known VB6, PHP & JS for a long time so my preference for formatting actually varies wildly between languages where I find what’s ‘natural’ for the language in question based on how wordy or dense it is.

In object-based languages, it’s nice to uses spaces between the parentheses as lengthy parameter lists can obscure the ‘scopes’ in play, PHP is a language where I like spaces for function calls.

I can’t say I’ve used Rust enough to grasp the right ‘feel’ for idiomatic formatting yet.


#9

f( x ) is very much not mainstream in my experience; I have actually never encountered it in the wild. I think that this is untenable; most people probably want to stick the mainstream function call syntax (because what you describe are function calls syntactically; the grammar doesn’t know about named tuples) and adding a switch for multiple styles to rustfmt seems very questionable…


#10

rustfmt’s style is configurable, but AFAICS there’s nothing for spacing parentheses (yet).


#11

Maybe I’m pointing out the obvious, but isn’t this why it’s convention to name enum variants PascalCase? Then, it’s never ambiguous what is a function, and what is a type or variant.


#12

TIL! (Also, apparently Discourse does not consider “TIL!” a complete sentence…)


#13

Rust already reserves lower_snake_case for function names and UpperCamelCase for tuples and enum variants and warns you when you’re writing a function in UpperCamelCase. So just by looking at Parser::parse(...) you should be able to tell whether you’re looking at a tuple initalizer specifically or a function call in Parser namespace. Adding spaces between brackets is redundant.