You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Panics in destructors can cause the tail expression of a block to be leaked #47949
structNoisyDrop;implDropforNoisyDrop{fndrop(&mutself){println!("dropping a NoisyDrop");}}implNoisyDrop{fnnew() -> Self{println!("creating a NoisyDrop");NoisyDrop}}structPanickyDrop;implDropforPanickyDrop{fndrop(&mutself){panic!()}}fnfoo() -> NoisyDrop{let p = PanickyDrop;NoisyDrop::new()}fnmain(){foo();}
Expected Result
"creating a NoisyDrop" and "dropping a NoisyDrop" should appear the same number of times
Actual Result
creating a NoisyDrop
thread 'main' panicked at 'explicit panic', src/main.rs:18:8
note: Run with `RUST_BACKTRACE=1` for a backtrace.
ping @rust-lang/compiler this should be triagged, even if it is not a soundness bug, unwinding working properly is something that a lot of code relies on.
fnfoo(){let n = NoisyDrop::new();let p = PanickyDrop;}
The specific scenario here is that:
we are in the middle of returning the NoisyDrop to our caller (so our caller "owns it")
but we panic before our return completes (so they don't realize they own it)
I don't think it's quite right to say that "we make no guarantee that destructors will ever be invoked". We don't guarantee that all destructors are always invoked, but we do guarantee some things: for example, that if you have a local variable whose scope includes a call, it will be dropped when the call panics (here, n would be dropped):
let n = ...;foo();
So the question is what we should guarantee in this particular scenario. Perhaps the answer is that we should just be very clear to document the current behavior, given how long it has existed.
changed the title [-]Panics in destructors can cause the return value to be leaked[/-][+]Panics in destructors can cause the tail expression of a block to be leaked[/+]on May 23, 2026
Activity
jonas-schievink commentedon Feb 1, 2018
Heh, this even happens on Rust 1.0
arielb1 commentedon Feb 2, 2018
@jonas-schievink
Rust 1.0 is far worse than this - it leaks everything when a destructor panics.
solb commentedon Mar 8, 2018
@arielb1, could you explain why this is tagged as a soundness bug? The language makes no guarantee that destructors will ever be invoked.
gnzlbg commentedon May 7, 2019
ping @rust-lang/compiler this should be triagged, even if it is not a soundness bug, unwinding working properly is something that a lot of code relies on.
nikomatsakis commentedon May 7, 2019
So, to be clear, it's not that we leak all destructors when panic in drop occurs. For example, this function drops both
pandnas expected:The specific scenario here is that:
NoisyDropto our caller (so our caller "owns it")I don't think it's quite right to say that "we make no guarantee that destructors will ever be invoked". We don't guarantee that all destructors are always invoked, but we do guarantee some things: for example, that if you have a local variable whose scope includes a call, it will be dropped when the call panics (here,
nwould be dropped):So the question is what we should guarantee in this particular scenario. Perhaps the answer is that we should just be very clear to document the current behavior, given how long it has existed.
63 remaining items
Auto merge of rust-lang#125923 - matthewjasper:no-return-leak, r=<try>
Auto merge of rust-lang#125923 - matthewjasper:no-return-leak, r=<try>
Auto merge of rust-lang#125923 - matthewjasper:no-return-leak, r=<try>
Auto merge of rust-lang#125923 - matthewjasper:no-return-leak, r=<try>
Auto merge of rust-lang#125923 - matthewjasper:no-return-leak, r=<try>
theemathas commentedon May 23, 2026
Possibly related to #156713.
[-]Panics in destructors can cause the return value to be leaked[/-][+]Panics in destructors can cause the tail expression of a block to be leaked[/+]theemathas commentedon May 24, 2026
A leak can also happen with a closure that panics upon drop, even without any relevant blocks.
The above code prints "created" but not "drop".
fix(rust-js): what a filter or a take_while discards is dropped past …
fix(rust-js): a collect std makes in place is its fold, so a filter's…
fix(rust-js): a collect in place asks no room: std's compiled collect…