> it doesn't even properly align the parenthesized expression after line-breaking it:
Fair enough if that's not your preference for how parenthesized expressions should be broken across lines, but this quote makes it seem like it's objectively wrong. In fact it's very much a matter of opinion, and personally I hate the style of line breaking that he describes as "properly" aligned because you end up with a distracting pattern of indentation, and parameters get all squished into a tall narrow column:
x = long_function_name(first_param,
second_param,
another_fn(x
+ y
+ z))
Yuck! In Clang-format, you can avoid this with the "AlignAfterOpenBracket: AlwaysBreak" option, which also wraps the first parameter / operand to the next line if it can't all fit onto one line:
x = long_function_name(
first_param,
second_param,
another_fn(x + y + z))
Just to reiterate: I appreciate that many people would still prefer the first style, and of course that's a legitimate opinion. My point is just that it's also a legitimate opinion to prefer the second style, not something you can describe as "not properly aligned".
Of course this is a nitpick on an excellent article.
This was the key line:
> I eventually gave up on trying to make the formatted rust-fuse code look pretty, and settled for "consistent".
It turns out that most aspects of coding style are subjective and are fine once you get used to them. The *-fmt tools that are now the fashion get everyone past that initial stage of formatting things as you personally would and get you used to a common style.
Maybe one person (the author of the style tool) is completely happy with the chosen style, but consistent is more important than personal preference on details.
One thing I've learned over the years using a lot of different languages is to pretty much ignore my initial reaction to syntax or formatting conventions for any given language - there is a pain period and then you get used to things and move on...
Edit: I'm particularly thinking of PostScript, Lisp and Python - which on first impression seemed crazy but I actually learned to really like.
I have a similar thing when I work on a new codebase (or join a new company for that matter): make a note of all the pain points, but don't do anything with them for a few months.
It'll turn out that a lot of them were for a good reason, but some of them won't be and the existing team has just got used to them ('missing stair'). That's why the notes are important, because you probably have too!
Mind you - I have joined places that were genuinely crazy (no source control, no backups, deployment by RAID...) where some things needed fixed immediately.
The company supplied on-premise server 'appliances' for a particular market niche.
To create a new deployment, find an existing server (usually at an existing customer), take out a mirrored RAID disk and replace it with a blank one. When mirroring complete put old one back in and take your new copy and use it in a new server for the new customer....
Repeatable build process, repeatable deployment process - who needs that when you have the power of RAID-1! <sob>
Wow. That's both amazing that somebody thought to do that and it worked, and terrifying that it became the official solution for production deployments.
This taboo of having opinions about code formatting shuts down legitimate criticism of rustfmt's design and implementation.
There are cases where rustfmt is just bad. I'd argue "objectively" bad:
• match arms are formatted individually, so nearly-identical arms can end up looking wildly differently if they hit different fuzzy logic thresholds. In Rust, enum variants aren't types, and macros can't see enum variants in the match context, so repetitive match arms are sometimes a necessary evil.
• deletion of a line can fold a multi-line construct into a single line. This makes diffs bigger and harder to review.
• rustfmt throws away any human influence on layout of code (short of sorting functions alphabetically). It doesn't merely change the boring bikeshedable mechanics like where the spaces go, but significantly rearranges how expressions are laid out and separated from each other.
For example:
let value = do().something().to().get().a_value_maybe()
.or_else(|| some_error_handling_here)?;
I think the code above is great, because the happy path is on one line, and error handling is on the other. Rustfmt intentionally erases that:
let value =
do().something().to().get().a_value_maybe().or_else(|| some_error_handling_here)?;
That is IMHO bizarre. The value is what? Oh, there's spaghetti in the second line.
Gofmt is way better. It fixes formatting errors it understands, but when there are multiple correct ways to format a piece of code, it leaves the higher-level structure as-is. OTOH Rustfmt has an overriding opinion on everything. Non-negotiable heuristics replace all common sense.
There's no other production-quality formatter for Rust, so usage of rustfmt boils down to:
1. We must use something
2. Rustfmt is something
3. Therefore, we must use rustfmt
Your formatting style works too, and rustfmt will switch to it for function calls that exceed the line width limit.
rustfmt is not properly handling alignment in combination with hard tabs. It should either break after the open paren and indent both names, or break near the '+' and align them both.
My personal impression from these examples you gave is that line-breaking inside an arithmetic expression is generally a horrible idea. If you have something like
let result = (first_long_variable + second_long_variable) * (third_long_variable + fourth_long_variable);
My position is that the only way to make this look good under a line length limit is
let factor1 = first_long_variable + second_long_variable;
let factor2 = third_long_variable + fourth_long_variable;
let result = factor1 * factor2;
(where of course `factor1` and `factor2` would be given names that make sense in context)
Thinking about it, you're probably right. One thing you're missing for your comment is different field name lengths, which is important because you seem to care about alignment of field values:
field1: 1
this_is_a_longer_field: 2
Playing with rustfmt online [1], I found that by default it doesn't do that:
field1: 1
this_is_a_longer_field: 2
As a sibling comment says, you must have an option turned on an option for this, and your complaint is that this option isn't working for these nested structs. I guess that's the risk of not using the formatter in its default configuration. But I agree it probably is a bug.
(You've presumably also got an option turned on for tabs. I honestly think that is quite a niche preference nowadays - cue flame war - so I can see that there would be bugs in that option too. But although you bring up tabs in your comment here and in your blog post, I don't see that it's related to the bug you're talking about. Once the formatter has (incorrectly, you argue) decided to format those lines as "one more level of indentation than the line before", it chooses the correct combination of tabs and spaces to do that.)
> You've presumably also got an option turned on for
> tabs. I honestly think that is quite a niche
> preference nowadays
gofmt always indents with tabs, so I'd argue that it's not _that_ niche.
One might also argue that, as a zero-cost abstraction for consistently applying a developer's preferred indent depth, they are the most Rust-appropriate option </s>.
> Once the formatter has (incorrectly, you argue) decided
> to format those lines as "one more level of indentation
> than the line before", it chooses the correct
> combination of tabs and spaces to do that.)
I'm not sure that's the case. It seems to be correctly aligning it when using spaces, so I think the issue is that somewhere it gets confused about whether to indent or align. A lot of very old indenters will treat tabs and spaces as interchangeable, which is why you see so much poorly-formatted C in open-source UNIX tools.
> A lot of very old indenters will treat tabs and spaces as interchangeable, ….
Indeed. If you're going to align expressions in a file that uses tabs for indentation then you need to use tabs up to the block indentation level, but spaces after that for the alignment. That's the only way to ensure that the lines continue to be aligned for any tab-stop setting. Many editors will mangle this horribly, however, by replacing some or all of the spaces with tabs. If you're using tabs for indentation then you really need to turn on visible whitespace and also disable automatic conversion of spaces to tabs unless you're sure your editor is one of the few that gets it right.
Edit: I just realised the example I showed is different in that it doesn't try to line up all the field values (for fields that do fit on one line) so it must actually have some other option set, like you said.
You mean never wrap code and have a really long horizontal scroll? These things are subjective and you're certainly entitled to your opinion, but is say that's fairly hardcore.
You could let the editor soft-wrap the lines in place of horizontal scroll. Done well this could be the best of both worlds—adaptive to different screen/editor sizes, and the viewer decides how to indent or align the wrapped lines according to their own preferences. However, proper expression alignment would require solid language support from the editor. (Wrapping with simple indentation wouldn't be too difficult.)
Fair enough if that's not your preference for how parenthesized expressions should be broken across lines, but this quote makes it seem like it's objectively wrong. In fact it's very much a matter of opinion, and personally I hate the style of line breaking that he describes as "properly" aligned because you end up with a distracting pattern of indentation, and parameters get all squished into a tall narrow column:
Yuck! In Clang-format, you can avoid this with the "AlignAfterOpenBracket: AlwaysBreak" option, which also wraps the first parameter / operand to the next line if it can't all fit onto one line: Just to reiterate: I appreciate that many people would still prefer the first style, and of course that's a legitimate opinion. My point is just that it's also a legitimate opinion to prefer the second style, not something you can describe as "not properly aligned".Of course this is a nitpick on an excellent article.