In Option::get_or_insert_with(), forget the None instead of dropping it.

This allows eliminating the `T: [const] Destruct` bounds and avoids
generating an implicit `drop_in_place::<Option<T>>()` that will never do
anything.

Ideally, the compiler would prove that that drop is not necessary
itself, but it currently doesn't, even with `const_precise_live_drops`
enabled.
This commit is contained in:
Kevin Reid
2025-11-05 18:49:44 -08:00
parent 90b6588979
commit c1b51dd002
2 changed files with 42 additions and 3 deletions
+18 -3
View File
@@ -1776,7 +1776,7 @@ pub fn get_or_insert(&mut self, value: T) -> &mut T {
#[rustc_const_unstable(feature = "const_option_ops", issue = "143956")]
pub const fn get_or_insert_default(&mut self) -> &mut T
where
T: [const] Default + [const] Destruct,
T: [const] Default,
{
self.get_or_insert_with(T::default)
}
@@ -1804,10 +1804,25 @@ pub const fn get_or_insert_default(&mut self) -> &mut T
pub const fn get_or_insert_with<F>(&mut self, f: F) -> &mut T
where
F: [const] FnOnce() -> T + [const] Destruct,
T: [const] Destruct,
{
if let None = self {
*self = Some(f());
// The effect of the following statement is identical to
// *self = Some(f());
// except that it does not drop the old value of `*self`. This is not a leak, because
// we just checked that the old value is `None`, which contains no fields to drop.
// This implementation strategy
//
// * avoids needing a `T: [const] Destruct` bound, to the benefit of `const` callers,
// * and avoids possibly compiling needless drop code (as would sometimes happen in the
// previous implementation), to the benefit of non-`const` callers.
//
// FIXME(const-hack): It would be nice if this weird trick were made obsolete
// (though that is likely to be hard/wontfix).
//
// It could also be expressed as `unsafe { core::ptr::write(self, Some(f())) }`, but
// no reason is currently known to use additional unsafe code here.
mem::forget(mem::replace(self, Some(f())));
}
// SAFETY: a `None` variant for `self` would have been replaced by a `Some`
+24
View File
@@ -495,6 +495,30 @@ const fn option_const_mut() {
*/
}
/// Test that `Option::get_or_insert_default` is usable in const contexts, including with types that
/// do not satisfy `T: const Destruct`.
#[test]
fn const_get_or_insert_default() {
const OPT_DEFAULT: Option<Vec<bool>> = {
let mut x = None;
x.get_or_insert_default();
x
};
assert!(OPT_DEFAULT.is_some());
}
/// Test that `Option::get_or_insert_with` is usable in const contexts, including with types that
/// do not satisfy `T: const Destruct`.
#[test]
fn const_get_or_insert_with() {
const OPT_WITH: Option<Vec<bool>> = {
let mut x = None;
x.get_or_insert_with(Vec::new);
x
};
assert!(OPT_WITH.is_some());
}
#[test]
fn test_unwrap_drop() {
struct Dtor<'a> {