Why is a temporary created here?

Hey folks,

I was playing around recently with the new let chains feature and read several old problems about let chains on GitHub. From this issue I found this code. Which generates the following compilation error:

   |
11 |     if let n = ()
   |            - binding `n` declared here
12 |         && let _ = B(&n)
   |                    --^^-
   |                    | |
   |                    | borrowed value does not live long enough
   |                    a temporary with access to the borrow is created here ...
13 |     {};
   |      -- ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `B`
   |      |
   |      `n` dropped here while still borrowed

Meanwhile I think I understand the drop order stuff about let chains and also the error in 2021 edition but what I currently don't understand is why the RHS of let _ = B(&n) results in a temporary in the first place according to the compiler error? I would understand when it creates a temporary for let _ = &B(&n) but not for let _ = B(&n).

Regards
keks

I don't know why B(&n) is referred to as a temporary in this context, but the destructor on B is problematic, because dropck doesn't understand what a destructor does. By default no reference stored in a type is allowed to dangle at the time the destructor of an instance of said type is executed. You can use the dropck eyepatch to pinky-swear to the compiler that you won't access a potentially dangling reference in the destructor:

#![feature(let_chains)]
#![feature(dropck_eyepatch)]
#![allow(irrefutable_let_patterns)]

struct B<'a>(&'a ());

unsafe impl<#[may_dangle] 'a> std::ops::Drop for B<'a> {
    fn drop(&mut self) {}
}

fn f() {
    if let n = ()
        && let _ = B(&n)
    {};
}

Playground.

1 Like

I don't know why B(&n) is referred to as a temporary in the error either (even if you bind it to a variable). Perhaps it's some drop elaboration issue? But I'm not sure how much time I want to pour into exploring that either, unless something analogous can be demonstrated on stable/edition 2024.


However, as to the titular question -- why is a temporary created?

Well, what is a temporary? It's a value not associated with a variable or other pre-existing place (like a derefence of a Box<_>). They have drop scopes, similar to how variables do. Otherwise their destructors would not get ran.

So these create temporaries:

   String::from("hi");
// ^^^^^^^^^^^^^^^^^^ a temporary 

// Same thing since `_` doesn't bind
let _ = String::from("hi");
//      ^^^^^^^^^^^^^^^^^^ a temporary 

The String is deallocated when the temporary drops.

And actually this does too:

let x = String::from("hi");
//      ^^^^^^^^^^^^^^^^^^ a temporary

The temporary just gets immediately moved into x instead.


And similarly with B(&n) (though again I can't give a satisfactory explanation for that exact error).

Being technical about it: temporaries are created when a value expression is in a place expression context.

1 Like

Thanks for your answers! Okay, that's reassuring, because I was starting to think I might have misunderstood something fundamental here. Perhaps the problem is simply that the compiler's explanation is unfortunate or something...