Linting and match ergonomics

A warning lint wouldn’t have to wait for a new edition though, right?

2 Likes

Not necessarily, but I don’t think we would pursue that particularly soon either with how new the feature is and how many other things we have on our plate.

2 Likes

Sure, I don’t expect it now. I’m kind of playing the long game here:

  • If there’s general agreement in core that adaptive matching in let might not be a good idea, it’s possible there will be support for an allow-by-default clippy restriction lint.
  • Due to the future clippy/rustc relationship, such a lint would require a stable way to detect if patterns are exact or adaptive.
  • That would mean a general adaptive pattern matching lint would be possible and stable in the future.

To me, a sentiment of “it might be good if it were possible for secondary tooling like clippy to be aware of adaptive matching” would already be a huge win.

2 Likes

I would be confident clippy will be able to understand patterns in that way some day, because I would be confident there will be some widely agreed upon code smell that involves adaptive patterns. The negative reaction I’ve seen on this and other issues in clippy has been to proposals to just disable whole features through clippy right after they’re stabilized. The clippy team feels that’s using clippy as a tool to express dissensus, which isn’t the purpose of clippy.

6 Likes

Honestly reading things like this hurts. It basically says all my reasonings for why I'd like to disable these things are lies.

I have a library I'd really like to release at some point. It contains some unsafe code. It already took me a while to be reasonably sure that it's safe. Especially in that context things like match ergonomics scare the crap out of me. An opt-in lint in a secondary tool would be a good place to increase confidence.

But I hope you're right that there will be some way to detect it. That gives me some hope.

2 Likes

A lie requires an intent to deceive, which was not apparent in @withoutboats' statement. As I understand it, "clippy isn't a tool to express dissensus" is a restatement of the more general principle: "Rust dialects are out of scope".

5 Likes

It implies that I have no good reason for wanting the restriction besides dissent.

There is also nothing here that would create dialects. Otherwise every lint creates a dialect and the point is moot.

2 Likes

If I were someone who used Clippy a lot (e.g., wired into my editor whenever I saved a file), then I would definitely appreciate lints like this, because they could tell me when I’ve written code that relies on a newer version of the compiler. (I write code with the nightly compiler but generally target much older versions of Rust.) Admittedly, Clippy is probably not the right tool for such things, but it would be convenient.

Orthogonally and aesthetically speaking, I think it’s perfectly reasonable to not want to rely on match ergonomics in all scenarios. I’ve sometimes disagreed with Clippy over things like lifetime elision where sometimes I want the explicit lifetime for purposes of clarity. However, I do admittedly make use of lifetime elision in the vast majority of cases.

I don’t agree with using Clippy as a hammer to form different Rust dialects or drive dissensus, but that seems like a tough call to make! It seems tricky to associate a specific lint with any particular intent to drive dissensus.

A fun anecdote that isn’t intended to make any sort of point: I’ve written ~20,000 lines of Rust over the past few months, mostly all on the nightly compiler, without ever bothering to check whether it worked on an older compiler. I just recently checked after all that time, and I found 3 different errors. Only one of them was due to match ergonomics. I thought for sure there would be more, but it’s amazing how much the old system has been ingrained into my head!

6 Likes

In some cases it would certainly be nice, but I'd prefer fine grained options. For example: I'll give explicit types to parts I consider important in unsafe code. If they all get type information, nothing stands out anymore. If there were a "be explicit about all bindings that are involved in unsafe code" lint I might activate that. But my main concern are references and lfietimes. That's why I love the '_ change for example.

The thing is: With things like match ergonomics, in-band lifetimes, impl Trait in argument position and all the other ergonomic local-information hiding features I already dissent by not using them. The lint has no influence on that. The lack of the lint only causes me pain, and will cause contributors pain.

Honestly I'm mostly hurt by the constant framing of the issue as me harming the community by people in leadership positions. That's why I'm so bitter.

Here's some current opportunities to create dialects:

  • Don't allow as conversions in your code.
  • Don't allow impl Trait in argument position.
  • Bounds go in where clauses, not in the <T> declaration.
  • Don't use ?.
  • Always use match over if let and be exhaustive.
  • Support an older version of rustc.
  • Be no_std.
  • Disallow unsafe.

But if you make these kinds of decisions you aren't villified.

At this point I'm honestly baffled. All these ergonomics changes work by hiding local information. Of course some people are going to be uncomfortable with that. And they focus on match ergonomics because that's the gravest one, since you might not even tell it's happening in a diff.

And since we're apparently now full in on calling people like me dissenters: I don't care about communicating my dissent through code. I just want the ability to enforce project standards, and not be told I'm a bad person for that.

It would be great if everyone assuming me and others are just dissenters would just try to assume good faith. Try to imagine if we could have non-ulterior motives here.

A final repeated plea to the leadership: Don't say people who don't like a feature are "dissenters". Don't accuse people of splitting and harming the community because they don't have buy-in to a feature. To me, this is even worse than people just not caring about my usecase.

And if people don't want a feature in certain or all contexts, try to understand why. I promise you we have reasons. We're not just assholes. If there are other solutions, we're happy. For example: The type description idea above for patterns would be great if coupled with a "no match ergonomics for explicit types" lint. It's one thing to disagree, or to not care, but please stop painting us as malicious.

8 Likes

I want to chime in here about clippy. While clippy has turned down lints for reversing match ergonomics (due to reasons I’ll explain below), we do have an open issue about addressing a specific local reasoning issue in a holistic manner. This will also lint rust 1.0 code which has the exact same local reasoning issues. All match ergonomics did was extend what the compiler already supported, and thus also extended some readability flaws.

Coming back to why we closed issues requesting the reversion of match ergonomics (and other features, too). A feature itself is not evil, bad, unreadable … Some uses of the feature are.

For example: we have a lint that lints calling clone() on a &&T, which is a useless operation because you just get another &T. This is a confusing interaction of the lack of autoderef and Clone impls for all Copy types. That doesn’t mean it’s bad for Copy types to implement Clone, or for references to implement Copy, or for users expecting autoderef to happen just because it exists.

We should be discussing how to prevent the bad cases without impacting the readable, convenient and absolutely correct cases. We shouldn’t be discussing which features are bad. That discussion can come if it is noticed that some features overwhelmingly collect lists of bad cases, and not just a few very visible cases that are surprising if your mental model of Rust hasn’t caught up with the current language yet (which mine definitely hasn’t. I still get surprised by features like match ergonomics a lot, but in the code I’ve written or touched I’ve so far only been positively surprised)

8 Likes

What if the use case is consistently marking destructured access as ref x, ref mut x, or x across related types in different modules? Some patterns might just be Foo { x, y }, some might be Foo { ref x, ref y }, and some might be Foo { ref x, ref mut y }?

In cases like that, I don’t think anything will beat a simple #![warn(flexible_patterns)] on the mod or fn. When it’s about clarity and convention in specific cases like unsafe code, or code where the kind of destructuring has semantic value, the fact that what is written is what’s actually there is what adds the value.

I’d of course love lints that guard against problematic use outside of that as well, where full strictness isn’t needed.

I’d like to emphasize: I don’t consider the feature bad or evil, and I’m not saying it doesn’t have upsides. I feel like I’m still fighting about recognition that there can be downsides in the presented use-cases.

For me, I have already decided that in contexts such as mentioned above, match ergonomics hides too much and I try to avoid it there. Keeping this a tedious, annoying process where I can never be sure I’m actually communicating the actual semantics won’t make me like the feature more. If I could lint in those cases (that might be as small as an unsafe function, or even a single match statement) I might actually start valuing it more in other cases, such as tuple arguments to closures, where I actually find it helpful.

1 Like

Somewhat of a meta-point but this thread is timely.

I remember when I first heard about Rust and shortly after having a presentation by Niko where I worked (his cousin was a co-worker at the time). I’m not sure whether this analogy is from that presentation or others during the same period, but I developed it anyways. The analogy is contrasting Rust with Haskell, given that Haskell’s unofficial motto is “avoid success at all costs”. The intent is to paint Rust as wanting to be successful but also as wanting to avoid Haskell’s approach to language design, as a research language. I remember a specific notion of only wanting to include things in Rust that had 20-30 years of experience and research behind them (and hopefully positive) before adding them to the language. This approach is motivated by being likely to avoid the issue of adding features which turn out poorly and make the language unsuitable for production use.

Perhaps I’m remembering incorrectly or perhaps I misinterpreted, but I used to view Rust’s approach to language design as being conservative, perhaps much like Lua’s. I wouldn’t expect all new features to require lots of experience and both Rust and Lua have added novel features (or feature combinations) with great effect. Unfortunately, I’m slowly gaining the impression that Rust’s focus is no longer on being conservative about language features. I would generally have expected features that primarily focus on adding ‘sugar’ (i.e. it adds an optional way of specifying something which can already be specified) to be less likely to be added, simply due to how difficult it is to remove a feature once added combined with the necessarily low benefit (compared to features which allow specifying new things entirely).

While I’m writing this in a thread about ‘match ergonomics’, I’m not interested in explicitly stating an opinion on that directly. Though I will express that due to this perceived change in focus, combined with the issues @phaylon mentioned regarding responses to people with good intentions when they disagree with a given viewpoint, I’m no longer as motivated to contribute to discussions, even if I think that my feedback could help the language.

1 Like

I'm working on an RFC for that.

I think that people in leadership and elsewhere have recognized that there are and can be drawbacks to match ergonomics. And to me, @oli-obk's comment right above yours reads like an admission of exactly that. We should work on building consensus around corner cases that we all believe are problematic and should be linted against.

I think what I and others would object to is people, who were against an RFC to begin with, trying to use Clippy as a tool to undue whole RFCs, as a way around the process, and not some problematic corner case of those.

1 Like

A lint is a tool that can be used in many different ways. You see it as "undoing" whole RFCs, others might see it as a tool to get back in-code information and make sure to keep it. I also vehemently object to phrasing it as "a way around the process". The feature is in, you won. A project deciding not to use a feature isn't undoing anything. The ability to not use a feature is part of the process, and such decisions should in my opinion be respected.

I'll refer again to my plea to assume we're arguing in good faith and don't have nefarious agendas.

I mean, what do you believe are peoples reasons for trying to "undo the RFC" as you put it?

1 Like

Usually legitimate concerns and drawbacks with the RFC. To be sure, most decisions in life and RFCs have trade-offs. My point is that we should try to identify those drawbacks and concerns we can all agree upon and try to deal with those with a lint if need be.

You may want to undue an RFC in good faith. Doing so is not nefarious if you believe it will hurt the community or your livelihood / projects personally. However, we can't keep on arguing forever.

I think there has to be limits on what we provide lints against. It would not be feasible to have a lint for every RFC. That would just create chaos.

I also wouldn't say that I won anything... It is not a contest and I wasn't personally invested in match ergonomics one way or the other (but I don't mind the outcome because it has been a nice experience so far).

Wrt. projects not using certain features, that is naturally your prerogative. I would just ask you (and I'm not saying that you aren't) to try to evaluate the feature with as open a mind as possible first.

If a lint is not provided in Clippy, it is possible for you to reuse Clippy's infrastructure and write your own set of lints against the particular features you don't approve of.

These are one and the same, when applied broadly enough. Trying to ban use of a feature in an entire codebase, for example, is precisely what we're trying to avoid. No, you personally don't have to write code that uses it---but we don't want to get to a place like C++ where every company has its own style guide with its own subset of features and you have to re-learn that "dialect" when switching codebases.

You also have to keep in mind the context that people actually are directly requesting that RFCs be undone, or made removable "on a per-crate basis," and your initial requests in this thread sounded remarkably similar to that.

This is an unhelpful zero-sum phrasing. If we're going to discuss this, it needs to start from the assumption that everyone is going to be working with code that is allowed to include match ergonomics somewhere.

To put it more positively, there's no need to convince anyone that there may be problematic cases here. We're all on board. What we do need is more examples of failure modes so we can get a clearer picture of what lints would catch them without leading to per-codebase or per-maintainer subsets.

I'll note that most of the concrete examples here are primarily about confusing error messages, not about code that compiles but does the wrong thing. Do you have any specific examples of that, particularly around the unsafe code you're worried about?

Or in other words, have you given match ergonomics a chance in your own work, or is this preemptive concern? Perhaps they will surprise you, and all the same errors will still be caught.

7 Likes

Calling people disagreeing dissenters, saying they're fragmenting the community, or saying we're trying to work around or undo the process is unhelpful.

How is that different from every other lint or style decision, like impl Trait in argument position? The only difference here is that with match ergonomics it's much more of a burden, and the value of knowing what's written is there doesn't exist without a lint.

It's about code clarity for me. I've argued that since the RFC first came about. All my examples were based on things where not having match ergonomics helped in the past, and using them has not changed my mind. My unsafe workings are recent, but I have the same concerns there.

Where have I asked for a lint against every feature?

Does clippy have external lint support? Or would this be a "forever be sentenced to live on nightly and keep tracking rustc" situation?

Like I've said before, I'm past the point of thinking that there will be a solution to the issues. If people aren't convinced "what's written is what's there" patterns have value after I don't know how many discussions it's not going to happen.

I just want to be able to mention them without being called dissenter or anything like that. Is that so much to ask? This all got started because I mentioned my happyness about the future maybe providing a way to detect match ergonomics in the AST.

I'm also way too exhausted to once again go through building up my use cases and examples where this is helpful. I stopped counting how often I did that and usually I'm just told the other side doesn't value the things I value, and that's that (paraphrased).

3 Likes

That wasn't the implication. I'm assuming the statement was about the increase of lint requests for undoing RFCs (explicitly in a wording like this) this year. Not just for the match ergonomics. If we deliver a precedent without having discussed all the angles, how can we argue against linting other features?

My impression was that such terms were not used from the start. I wholly disagree with marking either side of a discussion as "wanting to destroy the language" or acting in bad faith. I think the focus on the entire feature is what caused the lack of acceptance of there being a valid argument. This expanded into a lack of desire to attempt to figure out what the overall picture is.

I also think this is caused by the lack of nontrivial source examples with issues other than diagnostics failures (so with runtime issues that should have not happened). You have been asked multiple times to provide examples showing the problems. Not providing those examples but again arguing for the problems doesn't advance the discussion.

Thus: Could you provide source examples in the playground that compile, but have the wrong runtime behaviour? I realize I'm essentially asking for an underhanded rust contest entry, but that's exactly the reason why the contest existed.

5 Likes

That would be great if it were being discussed instead of being dismissed.

The big difference with match ergonomics, as mentioned many times before, is that it's invisible. In a diff, it's really hard to not let it slip in if you don't want it in a place. With things like impl Trait in argument position, it's easy to and flag see during review. Match ergonomics is invisible and non-avoidable. "You can just not use it where it doesn't fit" does not hold.

I have provided multiple examples of values that is provided in the past. I'm sure there'll be plenty of downsides mentioned in the RFC and other related discussions. People haven't even responded to the usecases I mentioned in this thread.

Yeah, sorry that barrier for me is a bit too high. I've been discussing this for what feels like years for now. You can probably take the lint that disables multiple impl blocks for a type. The advantage of knowing there's only a single one is similar to the advantage of knowing that a seen pattern represents the actual data.

If the priority is "make sure people can't express their dislike of a feature" above being able to work around it's downsides I have no chance to win here. Thus: Let's start from the bottom: Would people agree that it might maybe be possible that there are usecases where disabling the whole feature might possibly be the best solution for some people?

2 Likes

I agree that this is a possibility. But so far I've only seen patterns independent of their surrounding code that make it very hard to play around with.

I have been active on stackoverflow's rust section for a while, and while I can't talk about other language's sections, at least in the rust section questions without MCVEs (minimal compileable verifiable examples) often get closed, downvoted or simply no answers.

I understand that the picture is very clear in your mind, but you need to help us see this picture, too. And you can't do that by explaining to us that it's problematic in code you are not showing. This might work for other problems, but evidently it is not working in this case.

7 Likes