Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions library/core/src/option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1776,7 +1776,7 @@ impl<T> Option<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)
}
Expand Down Expand Up @@ -1804,10 +1804,21 @@ impl<T> Option<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 this 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 avoids needing a `T: [const] Destruct` bound and avoids
// possibly compiling needless drop code. Ideally, the compiler would prove that that
// drop is not necessary itself, but it currently doesn't, even with
// `const_precise_live_drops` enabled.
//
// 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`
Expand Down
24 changes: 24 additions & 0 deletions library/coretests/tests/option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down