Skip to content

lint against unexpectedly late drop for temporaries in match scrutinee expressions #93883

Open

Description

@yaahc

View all comments

This came up recently on twitter: https://twitter.com/fasterthanlime/status/1491803585385877519

Where @fasterthanlime ran into an unexpected difference in behavior that caused a deadlock.

The following code works fine:

use parking_lot::Mutex;

struct State {}

impl State {
    fn foo(&self) -> bool {
        true
    }

    fn bar(&self) {}
}

fn main() {
    let mutex = Mutex::new(State {});

    let val = mutex.lock().foo();
    match val {
        true => {
            mutex.lock().bar();
        }
        false => {}
    };
    println!("All done!");
}

But when the mutex locking expression is inlined into the match scrutinee expression it causes deadlock:

    match mutex.lock().foo() {
        true => {
            mutex.lock().bar();
        }
        false => {}
    };

Link to playgrounds

This deadlock occurs because temporaries created in a match scrutinee automatically have their lifetime extended for the duration of the match expression. This is done to allow match arms to borrow from the scrutinee expressions, but this surprised many of the users in the above twitter thread because foo returns a bool, so there is no borrow taken by any of the match arms. My understanding is that the current behavior was chosen because otherwise drop timing of match temporaries would depend on whether or not you create any borrows.

This issue has generated some discussion around the possibility of changing drop rules in an edition to more aggressively drop temporaries. In this issue I would like to avoid a discussion of that size and instead propose that we introduce a lint that warns when one could encounter potentially surprising drop behaviour. @oli-obk has volunteered to mentor this issue. He suggested that we produce a warning when a temporary with a Drop impl which isn't borrowed by any match arms has its lifetime extended.

Activity

  1. added
    A-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.
    A-destructorsArea: Destructors (`Drop`, …)
    E-mentorCall for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion.
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    E-help-wantedCall for participation: Help is requested to fix this issue.
    E-mediumCall for participation: Medium difficulty. Experience needed to fix: Intermediate.
    C-feature-requestCategory: A feature request, i.e: not implemented / a PR.
    on Feb 10, 2022
  2. eddyb commented on Feb 11, 2022

    @eddyb
    Contributor

    This is a duplicate of #37612 - but I am not inclined to immediately close, since the ideas here might cause more progress.

    But I do want to stress that this is an ancient issue. rust-lang/rfcs#210 describes not just the problem (surprising semantics around releasing lock guards - though the culrpit in that RFC is different from what's implemented today), but offers a solution (NoisyDrop/QuietDrop dichotomy, to control which types need linting), all the way back in the summer of 2014.

    The sad thing is that there really hasn't been much (any?) progress around that kind of categorization, even if it could've come in handy in more than one way (e.g. a semantic QuietDrop marker could opt into MIR optimizations moving the drop earlier).

    (A quick search finds some newer mentions of NoisyDrop but it's mostly a common name for "print on Drop" types in code snippets, not the marker trait concept from that old RFC)

    cc @nikomatsakis @pnkfelix

  3. oli-obk commented on Feb 11, 2022

    @oli-obk
    Contributor

    Related clippy issue: rust-lang/rust-clippy#1904

  4. PrestonFrom commented on Feb 12, 2022

    @PrestonFrom
    Contributor

    I'm relatively new to rust, but after reading the fasterthanlime blog post about this and the related issues, I'd be interested in taking a swing at it, assuming no one else has already claimed it. Would that be okay?

  5. oli-obk commented on Feb 12, 2022

    @oli-obk
    Contributor

    Yes! So we've set up some procedure for this to make sure other ppl will know you are working on it. Just write @rustbot claim on the issue and it will get assigned to you.

    On the implementation side there are a few ways to go about it, but you'll likely have the best experience by implementing it in clippy first (going from editing your lint to seeing it in action on a test is quicker in the clippy repo).

    Generic instructions to get you started are in https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md

    I do recommend using https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md#author-lint to get a picture of how some tests that you come up with look like (tho I am biased, as I initiated the author lint xD).

    In any case, you'll want to stay simple, so I recommend ignoring the actual match arms for now and only concentrating on the situation where the match scrutinee type has no lifetimes other than 'static. Now, instead of a big info dump, I'd say you look into these first steps and if you hit any point where you wonder "what next?" or have any questions at all, you open a thread on https://rust-lang.zulipchat.com/#narrow/stream/257328-clippy and ping me (@oli) on your question. If you prefer you can also PM me in zulip, though you'll get more eyes on it if it's a public thread.

  6. PrestonFrom commented on Feb 12, 2022

    @PrestonFrom
    Contributor

    Thank you for the detailed suggestions! It’s very helpful!

  7. PrestonFrom commented on Feb 12, 2022

    @PrestonFrom
    Contributor

    @rustbot claim

  8. nikomatsakis commented on Feb 14, 2022

    @nikomatsakis
    Contributor

    Cool! I'd love to see this get fixed. I would say that -- implementation wise at least -- we should do it with an attribute placed on the type, analogous to #[must_use]. This can in fact be a start towards that "noisy drop" distinction that @pnkfelix described oh-so-many-years-ago.

  9. 23 remaining items

  10. PrestonFrom commented on Jul 6, 2022

    @PrestonFrom
    Contributor

    That makes sense! To make sure I don’t forget to come back to this, Would that mean waiting until 1.63 or 1.64? Or am I misunderstanding what a release cycle is?

  11. flip1995 commented on Jul 6, 2022

    @flip1995
    Member

    The current nightly is 1.64, see https://forge.rust-lang.org/#current-release-versions

    This would mean to wait until the current nightly is 1.65, which on 2022-08-11

  12. flip1995 commented on Jul 6, 2022

    @flip1995
    Member

    However you can start with the uplifting work a bit earlier, so that the uplifted lint will get into 1.65 right away.

  13. PrestonFrom commented on Jul 6, 2022

    @PrestonFrom
    Contributor

    Thank you for the explanation!

  14. leontoeides commented on Feb 24, 2024

    @leontoeides

    This lint saved my hide and helped me identify and correct a longstanding issue. Thank you for this lint.

    I feel like this lint should be elevated or even be made into a compiler warning/error. Unexpected deadlocks arguably undermine rust's claims of safety and fearless concurrency, especially for something so seemingly innocuous as a match on an Option or Result

  15. oconnor663 commented on Sep 8, 2024

    @oconnor663
    Contributor

    Agreed with @leontoeides. I just ran into this footgun, and I was surprised that rustc and clippy didn't seem to have anything for it. For other folks like me finding this thread late, the lint that @PrestonFrom added is https://rust-lang.github.io/rust-clippy/master/index.html#/significant_drop_in_scrutinee, and it's currently disabled by default. You can try it with something like this:

    cargo clippy -- -D clippy::significant_drop_in_scrutinee
    

    In my specific case I'm retaking the same lock behind a function call, and I'm especially happy that the lint still fires for me. (For reference here's my corrected code, with comments about how while let is tempting but broken.) I would love to see this warn in rustc by default. I'm sure I've written "correct" code that still trips this lint, and that might make it feel noisy in some cases, but I'm completely convinced that it's worth it. It is rarer that folks run into this, but when it comes up it feels on par with "unused Result that must be used" in terms of how essential it is.

  16. PrestonFrom commented on Sep 8, 2024

    @PrestonFrom
    Contributor

    Hi,

    Sorry, this is on me. Having a second kid really sucked up free time. I'm not sure what needs to be done to get it enabled by default but I'll look into it and get it moving again.

  17. oconnor663 commented on Sep 8, 2024

    @oconnor663
    Contributor

    Wow that was a lightning fast response :-D If you'd rather someone else take the ball, please let me know! I've been meaning to learn more about how Clippy works anyway :)

  18. PrestonFrom commented on Sep 8, 2024

    @PrestonFrom
    Contributor

    I'd like to finish it since I started it, and I'm getting some free time, but if it looks doubtful, I'll ping you here! I appreciate the offer very much though.

  19. flip1995 commented on Sep 16, 2024

    @flip1995
    Member

    A good starting point to see what needs to be addressed is the PR moving the lint to nursery: rust-lang/rust-clippy#9302 Some of those issues might already be fixed. But in order to move it back to a warn-by-default group, we need someone to go through all the issues that were raised and address them, either by showing that they were already fixed, fix them, add a test, or explain why this shouldn't be a blocking issue.

    Running lintcheck for the lint is also always helpful. We're doing this on CI now, but I'm not sure, if you'll see any different results by just changing the lint group.

  20. PrestonFrom commented on Oct 31, 2024

    @PrestonFrom
    Contributor

    @oconnor663 I thought I would have more time to work on this but I haven't yet. If you wanted to take over, I would be happy to step aside. Otherwise, I'm hoping to have more time in a few months.

  21. academician commented on Feb 21, 2025

    @academician

    It's worth noting that this behavior can now be fixed trivially in the 2024 edition by wrapping the scrutinee in a block scope. This is because of the new tail expression temporary scope behavior. Example:

  22. PrestonFrom commented on May 30, 2025

    @PrestonFrom
    Contributor

    @kyle-leonhard Yes! Thank you for offering -- I'm not really in a good position to work on this now. Please feel free to take over.

  23. git-staus commented on Sep 10, 2026

    @git-staus

    @PrestonFrom could it be that you pinged the wrong user? @kyle-leonhard has not been involved in this issue at all as far as I can tell.

    Also, since clippy::significant_drop_in_scrutinee is already present in Rust (albeit opt-in), is there any benefit of keeping this issue open?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

A-destructorsArea: Destructors (`Drop`, …)A-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.C-feature-requestCategory: A feature request, i.e: not implemented / a PR.E-help-wantedCall for participation: Help is requested to fix this issue.E-mediumCall for participation: Medium difficulty. Experience needed to fix: Intermediate.E-mentorCall for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions