Skip to content

if let should pre-drop wrt else #103108

Closed
Closed

Description

@est31

Consider this snippet:

struct Foo<'a>(&'a mut u32);

impl<'a> Drop for Foo<'a> {
    fn drop(&mut self) {
        *self.0 = 0;
    }
}

fn main() {
    let mut foo = 0;
    
    if let Foo(0) = Foo(&mut foo) { // Doesn't compile
    } else {
        *&mut foo = 1;
    }
    
    if matches!(Foo(&mut foo), Foo(0)) { // Compiles
    } else {
        *&mut foo = 1;
    }
}

Here, if let complains about an access of foo in the else branch, while if is okay with this.

This is due to if let dropping its temporaries after the else block. This is not neccessary, it could also drop them before.

Similar to #103107

cc @Nilstrieb

@rustbot label T-lang

Activity

  1. Noratrieb commented on Oct 16, 2022

    @Noratrieb
    Member

    This is probably a leftover from the time when if let used to desugar to a match, which keeps the scrutinee temporaries alive until the end of the match.

    Dropping them early would be more consistent with if (it cannot be quite consistent as if let needs to keep them alive during the if block) but less consistent with match. Since if let is closer to if than match, I agree that it should behave more like it.

    But I don't think we can change this. It could break code in extremely subtle undetectable cases (think of some kind of lock being held in the condition which is then required in the else branch). This also makes it hardly editionable, from a migration and also from a future rustc maintenance perspective.

  2. est31 commented on Oct 17, 2022

    @est31
    MemberAuthor

    Good point about the consistency. There are analogous drop order optimization for matches too: You can drop the scrutinee temporaries before entering the body of any arm that is binding free and only followed by other binding-free arms, so cases like:

    match foo {
        Enum::ValA(a) => {},
        Enum::ValB => {},
        _ => {},
    }

    for example, you would drop the temporaries before entering the bodies of the ValB and _ arms. Applied to if let, this would mean always dropping before else, and iff the pattern is binding free, also dropping before the then body.

    But even without that extension, I'd be okay with the inconsistency with match if it makes more programs compile and drops resources more early on.

    You are right, code might depend on the current behaviour and it might cause slight bugs. OTOH, one could also argue that if you rely on that specific quirk of drop order, then maybe your program was buggy from the start. <insert spacebar heating xkcd> :).

  3. added 2 commits that reference this issue on Sep 12, 2024
  4. est31 commented on Nov 7, 2024

    @est31
    MemberAuthor

    The given example compiles now on edition 2024, I suppose since #131984?

    Also, #131154 contains a lot of useful discussion about this question.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-let-chainsArea: let chains (if-let, while-let, ...)F-if_let_guard`#![feature(if_let_guard)]`T-langRelevant to the language team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions