Does anyone use a customized configuration for rustfmt?

Does anyone use a customized configuration (rather than the default config) for rustfmt? If so, mind to share your configurations?

I personally found the default behavior of rustfmt to be a little bit odd. Sometimes a long line is break into too many lines while sometimes a long line is not.

1 Like

In Tokio we just use the default config.

Can you give concrete examples? Identifying edge cases for the formatter would be really helpful.

Here is what I use for my code (including the time crate)

newline_style = "unix"
use_field_init_shorthand = true
use_try_shorthand = true

# Unstable features below
unstable_features = true
version = "Two"
comment_width = 100
error_on_line_overflow = true
format_code_in_doc_comments = true
format_macro_bodies = true
format_macro_matchers = true
format_strings = true
imports_granularity = "Module"
group_imports = "StdExternalCrate"
normalize_doc_attributes = true
wrap_comments = true

It seems like most big projects have decided that any advantage from a customized configuration is outweighted by the potential annoyance of people used to formatting under the default, especially if they have it setup to automatically apply whatever the default is.

Personally I just don't use rustfmt unless forced, since I disagree with what seem to be fairly core choices in how formatting is applied.

6 Likes

Same here, I don't like many of the defaults, and I personally value beautiful, readable, hand-formatted code over 100% enforcing conventions. If I work on a team, I have no problem understanding other people's code just because of slightly different formatting, and usually experienced programmers will follow the overwhelming majority of conventions considered idiomatic (eg. in the questions of whitespace and capitalization) anyway.

5 Likes

I generally find automation really useful for enforcing good code style. Sometimes rustfmt or clippy want something that is somewhat sub-optimal, but I don't think it's that often, and I think it's worth it when you get automation of code style in exchange.

I don't want to spend time on reformatting imports to be alphabetical, or on telling people how to properly format their where bounds on their functions.

20 Likes

I use the same config for all my personal projects:

hard_tabs = true

(but I do not want to start a tab vs spaces here, all the arguments of both sides are already somewhere on this forum)

2 Likes

I use the following across most of my projects ("most" because I may forget to update some of the older ones):

imports_granularity = 'Crate'
reorder_impl_items = true
use_field_init_shorthand = true
format_code_in_doc_comments = true
wrap_comments = true
edition = "2018"
version = "Two"

Usually I add configuration tweaks when the default doesn't wrap beyond half a screen, or non-default config is needed to handle async syntax, but otherwise normally try not to stray from defaults.

I always have a .rustfmt.toml in my personal projekts, but it is short, just one setting:

max_width = 78
1 Like

rust-analyzer uses the following config:

reorder_modules = false
use_small_heuristics = "Max"

rustc uses

version = "Two"
use_small_heuristics = "Max"
merge_derives = false

EDIT: and for blogging specifically I use

max_width = 65
tab_spaces = 2
use_small_heuristics = "Max"
1 Like

I do have many issues with rustfmt making odd choices, but I haven't found any configuration that makes it better.

What I do instead is from time to time I apply rustfmt and only selectively keep changes that make sense (with a git client that has a gui for git add -p).

The problem is that rustfmt wants to make a "canonical" version of formatting, so it ignores how code looked before.

My main gripe is that rustfmt will actively work on making short lines longer, which makes spaghetti code. But lowering line length limit forces it to wrap a lot more, and it's not good at wrapping. There's no option to "keep short lines as they are, only wrap lines that are too long".

If I write (imagine this example is closer to 80-column lines)

let foo = bar.try_something()
  .or_else(handle_error)?

If I allow long lines, it will be actively unwrapped to be as long as possible, which hurts readability:

let foo = bar.try_something().or_else(handle_error)?

If I ask for shorter lines, I can only get this weird hiccup:

let foo = 
    bar.try_something().or_else(handle_error)?

or it explodes vertically:

let foo = bar
    .try_something()
    .or_else(handle_error)?

There's no option in rustfmt to tell it to leave it as it was, with success case on one line, and error handling on the other.

8 Likes

I normally make my code fit into windows with a width of 80 characters, but since I started programming Rust (and use rustfmt with the default settings), I changed my window size to 100 characters.

When creating code to be pasted here (in this forum), I sometimes set max_width = 80. Not sure how many characters fit into one line in this forum (without scrolling), and whether it depends on the browser/platform/screen of the visitors.

Let's test:

1234567_10_234567_20_234567_30_234567_40_234567_50_234567_60_234567_70_234567_80_234567_90_23456_100_23456_110

I never really looked into the other settings, but I feel overall happy with the output of rustfmt.

1 Like

My firefox shows maximum 76 chars in your test before I need to scroll.

1 Like

I also see up to 76 characters, but if there's a vertical scroll bar, it might be less. I guess 72 chars should be safe (including a bit of margin).

Fwiw, on mobile, I only get about 36. I dunno if mobile considerations should play a role here but figured it's worth mentioning.

Personally, I put this rustfmt.toml in my projects:

max_width = 79

I think it's important any custom config is on a project-basis rather than global, so contributors all end up with the same settings. Is there a way to put a rustfmt.toml that disables the formatter? That way a maintainer can "opt-out" of contributors' autoformatters? or perhaps we should convince people to change our format-on-save scripts to only run if there exists a rustfmt.toml in a parent folder?

I don't think formatting matters. There's so much to worry about when writing and reading code, then any thought put into formatting is a waste of time. Even among rather superficial considerations: good names, good comments, good API documentation - far more important than any formatting decisions.

I consider the whole "code looks better/worse" a completely subjective preference. I mean ... on a daily basis I have to read code written in JS, TS, Java, Python, Bash, Rust, Nix, Fish, some random Yaml DSL... . Everything is completely different in every thing I touch, not even superficially. I jump into antother random thing, I look at and I do just fine, and then someone says that "this code is hard to read because stuff has longer/shorter lines than they would ideally prefer"? Come on.

The only thing about formatting that matters to me is so it doesn't get in a way and waste my time: people arguing about, people introducing noise in the commits because they change it, people asking me to go back to my PR and tweak formatting nits.

I love rustfmt and apply it on everything precisely so I don't have to think and talk about it. If a project has a rustfmt.toml it will do whatever the owner wants.

6 Likes

I use imports_granularity = "Crate" in my own projects. I also recently contributed a new option group_imports = "One"* that I'll start using as soon as it's released. So far my only issue with rustfmt has been that it's very lax regarding the formatting of imports, I'm fine with all the other defaults and I greatly appreciate not having to worry about the formatting of my code so much.

*the option will format

use super::update::convert_publish_payload;
use chrono::Utc;

use alloc::alloc::Layout;
use juniper::{FieldError, FieldResult};
use uuid::Uuid;

use std::sync::Arc;

use broker::database::PooledConnection;

use super::schema::{Context, Payload};
use crate::models::Event;
use core::f32;

to

use super::schema::{Context, Payload};
use super::update::convert_publish_payload;
use crate::models::Event;
use alloc::alloc::Layout;
use broker::database::PooledConnection;
use chrono::Utc;
use core::f32;
use juniper::{FieldError, FieldResult};
use std::sync::Arc;
use uuid::Uuid;
1 Like

I personally actually like that rustfmt canonicalizes the style. This for example means that if I write some long code that needs multiple lines and then make it shorter such that it fits on a single line, rustfmt will make it fit on a single line, just as it would if you wrote the short code in the first place. It also reduces the risk of conflicts when rebasing or merging and reduces diff sizes if I run rustfmt on all commits. (eg using git filter-branch)

1 Like

I don't know if I'm the only one but I use fn_args_layout = Vertical. I find it more readable even for short argument lists.