The thing about Rust abstractions is that they’re a lot more useful and forgiving than C++.
Eg: In Rust, I cannot accidentally use an option incorrectly and have the program compile. When it fails to compile, there’s a good chance the compiler will suggest how I could do what I wanted to do.
In C++, if I dereference an optional without checking it, I’ve triggered “undefined behavior”. The program will probably just segfault, but it could also return a bogus object of uninitialized memory, but technically it could overwrite my boot sector or call a SWAT team to raid my house, and still be in compliance with the C++ spec.
Thus when considering code written in Rust, I mostly need to just consider the happy path. With C++ I need to pedantically check that they used the constructs correctly, and consider many more runtime paths due to how lax the language is.
If I see someone dereference an optional without an if-guard, I now need to backtrack through the logic of that entire function to make sure the program doesn’t crash. If I see someone use a destructured value from an Option in Rust, I can rest easy that unless they used unwrap() somewhere, the compiler has done that for me.
This scales well for larger abstractions, because if I’m not actively digging into some code I can treat it more as a black box where it interacts with the code I am working with, than as a box of firecrackers that might explode if I do something unexpected with it.
I don’t want to waste my time either setting up multiple linters or having to drill down into the pros and cons of each. If the C++ community cannot even reach a consensus on which linter it endorses, I imagine it can’t reach a consensus on what it lints, which involves even more decisions.
Secondly, both times I’ve tried to roll out or use a linter, I’ve encountered passive or active resistance from the other developers on the team.
This resistance went deeper than the linter. On one team they didn’t want to use new language constructs from the last decade, on the other team they explicitly complained about me doing things differently than 15 years ago. In both cases they rejected what I understood to be the core C++ guidelines in favor of writing their own codebase-specific coding guidelines so they could pick and choose the constructs they understood rather than trying to adhere to what might be idiomatic for a particular edition.
Unless something is 100% endorsed by the C++ community, it’s absolutely not something that I’m even going to try to champion. I’ve already been flat-out told “no one cares about your opinion” trying to explain how type-safety in C++ can improve readability in code reviews, which I thought was completely noncontroversial.
To your second point, the point of linters is to guide code to be more idiomatic; it’s not an issue of language design, but of educating humans in mostly non-functional readability and best practices.
The thing about Rust abstractions is that they’re a lot more useful and forgiving than C++.
Eg: In Rust, I cannot accidentally use an option incorrectly and have the program compile. When it fails to compile, there’s a good chance the compiler will suggest how I could do what I wanted to do.
In C++, if I dereference an optional without checking it, I’ve triggered “undefined behavior”. The program will probably just segfault, but it could also return a bogus object of uninitialized memory, but technically it could overwrite my boot sector or call a SWAT team to raid my house, and still be in compliance with the C++ spec.
Thus when considering code written in Rust, I mostly need to just consider the happy path. With C++ I need to pedantically check that they used the constructs correctly, and consider many more runtime paths due to how lax the language is.
If I see someone dereference an optional without an if-guard, I now need to backtrack through the logic of that entire function to make sure the program doesn’t crash. If I see someone use a destructured value from an Option in Rust, I can rest easy that unless they used unwrap() somewhere, the compiler has done that for me.
This scales well for larger abstractions, because if I’m not actively digging into some code I can treat it more as a black box where it interacts with the code I am working with, than as a box of firecrackers that might explode if I do something unexpected with it.