-
Notifications
You must be signed in to change notification settings - Fork 56
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
match #34
Comments
I pretty much always use commas, I've found this behavior of
I know I use |
Regarding the comment about "no single-line blocks", I think single-line blocks are fine for short cases like Also, I don't know what rustfmt currently does, but in the same spirit as writing let x = long_expr
+ long_expr_2; (with the match expr {
pattern1
| pattern2 => ...
} |
To expand on my reasoning - I find block + comma noisey and prefer to avoid this kind of duplication where possible (by analogy to e.g., parens in
My analogy here is that for a function we don't usually write
So, it feels weird to explicitly write the I also think that the intuition of an empty match arm is "do nothing" (i.e., execute an empty block), rather than "reduce to a value which is void", in particular because it is rare to assign the result of a match with void type into a value. I also argue by analogy to block expressions in statement position - you wouldn't write
Yeah, this is specifically about the arm body and whether that should be a block. I don't consider this applying to either
Hmm, this might get into a wider discussion so it might be worth peeling off. My opinion is that I prefer binops to be on the previous line rather than the next one. I don't feel too strongly about that, but I do feel strongly that commas should follow that behaviour, and I kind of want |
I agree completely regarding commas, and semicolons; those operators should fade into the background as simple terminators, though. Regarding other binops (and I do think we should split this discussion off): I find that putting a binop at the end of the previous line makes it easier to get lost, and harder to construct the tree in your head, especially when you break an expression in multiple places. If I look at something like this: if really_complicated_expression
|| another_complicated_expression
|| yet_another_complicated_expression (where the complicated expressions themselves have piles of operators and calls in them), having the Typically, a comma or semicolon has importance because of its presence or absence, but for most binary operators, I need to know which operator (such as |
I find the lack of consistency more noisy than the extra character. Every time I use JSON, it trips me up that I can't include the trailing comma. There's also the usual diff arguments, etc.
This might be the root of it, yeah, I think I do think this way.
I do actually write this a lot; it took a lot of habit-breaking to get out of it. I'd probably keep it in, but this isn't an issue about that 😉 |
There is probably a dedicated RFC, but honestly, I don't like this syntax:
I much prefer:
I think it's visually clearer they are all parameters to the function, and where the list ends. I don't really mind the vertical spacing. Sometimes I even leave blank lines between conditions to make them stand out more. I really dislike the alignment in this:
|
Yes, there will be. For the match arm formatting I don't think it matters which of those we choose, but the difference between block and visual indenting might affect our decision. |
I too, prefer the use of commas even for arm blocks. I like the consistency and that you can visually differentiate the closing match arm from any other closing block. The comma is one of the things I use to help distinguish between arms, especially when the arms are relatively long. It would be a shame to loose that IMHO. |
Some discussion from the Rustfmt repo: rust-lang/rustfmt#1129 and rust-lang/rustfmt#490 |
I often have match arms like this:
Putting all the individual constants on a single line as proposed above takes up too much space IMO. |
@jethrogb To me, that seems like a place to extract that into a function to check those conditions, rather than requiring the formatter to support that style. |
@chriskrycho I have an (error) enum with 100 variants, whatever I do, at the end there will be a match that looks like that. |
I don't want to further derail this thread, but I'd be inclined to write a function that did something like this, instead of that long hairy match, if you need something to handle a bunch of different cases: fn is_error_blah(&variant: MyError) -> bool {
let allowed_variants = [MyError:A, MyError:B, MyError:C, ...];
allowed_variants.into_iter().any(|v| v == variant)
} But there will inevitably be some long lists in a match, so… ¯\_(ツ)_/¯ I see your point. |
I don't think there is a diff argument in this case - with a block you don't need a comma in any case, so adding or removing an arm has the same size diff with or without the extra comma. |
My opinion is that there will always be edge cases where mechanical formatting does not work. Either you put such functions in a module by themselves and treat them like generated code, or you mark them with |
I think this is ready for FCP. There is maybe a little more room for fine-tuning, but there hasn't been much movement recently. |
I'd like to toss my opinion in about "multi-line single expressions": The issue with turning all multi-line single expressions into a block in the arm means that a certain pattern becomes noisier: Preferredlet enc = match subject {
cond1 => Encoder {
field1: 5,
field2: cond1,
// ...
},
cond2 => Encoder {
field1: 11,
field2: cond2,
},
// ... etc
} Current Rustfmt, worse (imo)let enc = match subject {
cond1 => {
Encoder {
field1: 5,
field2: cond1,
// ...
}
}
cond2 => {
Encoder {
field1: 11,
field2: cond2,
},
}
// ... etc
} |
@seanmonstar this is covered here - #61 (comment) - and I think we are in agreement |
If one of the match patterns is formatted as a block, then rustfmt could prefer to format the remaining patterns as blocks. The following code doesn't look pretty let bar = match foo {
Foo::X(v) => { this_expression_is_not_long_enough_to_be_formatted_as_a_block(v) },
Foo::Y(v) => {
this_expression_is_longer_so_it_is_long_enough_be_formatted_as_a_block(v)
},
} This idea would break some diffs, but it could make the code look prettier - But perhaps it is better to wait and see if this idea is really needed. |
@nielsle I think it's fine to have some match RHSes formatted on one line and some not. I regularly see matches with several trivial cases and then a couple of complex ones, and I'd rather not see the trivial cases pushed to multiple lines just because of a single complex case. (In addition to, as you pointed out, causing cascading changes in diffs.) |
I would like to argue against treating block arms and non-block arms differently in the use of commas. To me, a block is just the special case of an expression that's longer, or, more stringently, an expression which is more conveniently expressed by using a few statements. I also find the inconsistency jarring -- are commas required/preferred after block arms or not? Fragmenting this further does not make a whole lot of sense to me. |
I wonder if we want to do something to handle cases like this: rust-lang/rustfmt#1807 (that is many small variants in a pattern)? It does seem bad to put each on their own line like this. It is not uncommon to see matches like this (but I don't think it is the common case). |
So, during the discussion with the team, we came up with a grammar that, we think, encapsulates the cases where we want "mixed-line" formatting:
Any patterns which are exclusively variants of |
Is there any particular rational for breaking multiple sub-patterns after the The currently suggested style means my eyes have to dart back and forth between beginning and end of lines with varying length to find the last relevant pattern. It also makes missing that multiple patterns are part of the same Having the |
For the right hand side of match arms, I think that a rule like If an arm is long enough to be broken across multiple lines, it should use block syntax. would be good. |
(I'd also argue for blocks everywhere, because I dislike non-block RHSes, but that's likely to be unpopular) |
@Florob if you mean something like this:
... I agree. Keeping the operator on the left helps a lot in understanding what's going on, with a block too:
(Yes, I also prefer to have a comma after the blocks :P ) |
@Alex-PK That is probably another case where it makes sense. My thinking was more along the lines of:
vs.
I find the later much either to scan through. |
Yes, I agree. As I said, I would also put the arrow on a new line too, as it's even easier to read/find the proper action performed. |
@Florob I didn't realize we broke after the (Closely related: while I don't think we want rustfmt introducing rustc version compatibility issues by using leading |
@joshtriplett I am rather confused by your parenthetical. |
With that, https://play.rust-lang.org/?gist=9d1547364870090d37cbb274ce808da6&version=nightly would work, but it does not yet. |
Wildcard patterns must to be the last anyways, so should be no comma in this case? match f(t) {
y if y > 400.0 || y.is_nan() => println!("Too large"),
y => println!("{}", y) // no comma here
}
match f(t) {
y if y > 400.0 || y.is_nan() => println!("Too large"),
_ => println!("wildcard") // no comma here
} |
basics
Spaces and newlines as shown. In particular, prefer not to line-break the discriminant if possible; each match arm must have its own line; arms are block indented.
simple arms
Short arm:
Prefer the above form if the arm fits on one line.
Avoid single line blocks:
The last match arm should have a comma if the body is not a block:
Block body:
Note no comma if using a block, prefer not to line-break pattern or
if
clause, block indentation of arm body.Empty blocks:
Note
{}
not()
, no comma, no space in the block.Question - multi-line single expressions
Should a block be required?
E.g.,
Rustfmt currently does the latter, I found that it sometimes makes it easier to read, helps prevent rightward drift, and gets around the slightly unfortunate issue of expressions with blocks needing a comma:
I'm no longer 100% convinced this is worth the extra vertical space however. We might want to leave this question unanswered for a while since the benefit in preventing rightward drift would not be so important if we pick a style which other wise prevents it (e.g., by using two space indent, or using more block indentation):
Breaking the arm
Prefer breaking before the body:
or
Then prefer breaking before the
if
(if present, see below).Then prefer breaking the pattern at
|
s (if there are multiple sub-patterns, see below). If breaking the pattern, the rest of the arm should only be broken as necessary. E.g., the following are ok:Finally, break each pattern if necessary.
Breaking before
if
If necessary to break before the
if
, it should be block indented, a block is required for the arm body, and the{
of the block should be on a newline. This helps prevent theif
clause be distinguished from the arm body. Never break after theif
:If
$expr
does not fit on one line, break it according to the rules for expressions.large patterns
If using a compound pattern (
pat1 | pat2
), prefer to put the whole pattern on one line. If it does not fit, put each sub-pattern on a new line, with the line terminated by|
:If any sub-pattern does not on the line, break it according to the rules for patterns.
Question - blank lines between arms
These are sometimes useful for indicating groups of arms, I propose Rustfmt leaves single blank lines where the user writes them and coalesces multiple blank lines into one. E.g.,
Question - consistency between arms
Should we allow a mix of block arms and single expression arms? Or should we use blocks for every arm if any arm requires it? The second alternative is more consistent, but wastes some space. It also looks odd in the relatively common case where there is a single large arm and many small arms.
Question - exhaustiveness
Should we offer any guidance about using
_
in match arms? I feel there are strong idioms for using this or not depending on desired backwards compatibility. However, it may be beyond the remit of the style guide/process.The text was updated successfully, but these errors were encountered: