Avoiding Option::unwrap after Option::replace


I have some code of the form

if let Some(x) = some_option.replace(X::new()) {
  // do some stuff
  .map(|new_x| new_x.something())
  .expect("won't fail because of the replace call above")

I’m trying to avoid the expect, which seems like it should be doable. But I can’t figure out how.
I can replace the .replace() call with .take(), and then set the option later, but that still leaves me with lifetime issues since setting some_option = Some(new_x) requires moving new_x, so I can’t then return new_x.something() which requires a mutable borrow of new_x.

Is there some way to chain the setting of an option that returns a reference to the new value?


This is by no means a perfect solution, but: Option::get_or_insert_with is really useful in situations like this.

The problem with get_or_insert_with in your situation is: you can’t easily tell, from the outside, whether it is get-ing or insert-ing. Here’s a version that uses a mutable local variable to track that; I consider it somewhat ugly because it mentions the new value twice.

    let mut inserted = false;
    let option_ref = some_option.get_or_insert_with(|| {
        inserted = true;
    if !inserted { *option_ref = X::new() }

get_or_insert_with works perfectly, and I don’t even need to distinguish get vs. insert since a previous take call will guarantee insert:

if let Some(x) = some_option.take() {
  // do some stuff

I missed it in the docs because I only searched for “-> T” instead of “-> &mut T”. Now I know to search for more variants :slight_smile:


While I do agree such unwrap/expect is ugly, I’m pretty sure that unwrap-to-panic code will be optimized out in your first example. You’ve written some_option's enum tag as Some, and compiler can see that nobody changed it since unwrap, so it’s trivial for compilers that panic-arm is dead code.


get_or_insert[_with] is a std-featured solution that almost gets the job done, but I too have been in this situation, and I really wish there was a

fn put (self: &'_ mut Option<T>, value: T>) -> &'_ mut T

in std.

1 Like

Your put looks like Option::get_or_insert.


Not exactly, get_or_insert either gets or inserts :sweat_smile: I am talking about directly getting the insert part:

    pub fn get_or_insert(&mut self, v: T) -> &mut T {
        match *self {
            None => *self = Some(v),
            _ => (),

        match *self {
            Some(ref mut v) => v,
            None => unsafe { hint::unreachable_unchecked() },

could become

    fn get_or_insert (self: &'_ mut Self, v: T) -> &'_ mut T
        match *self {
            | Some(ref mut inner) => inner,
            | None => self.put(v),

And yes, put could be renamed to insert

1 Like
pub fn put(&mut self: v: T) -> &mut T {
    *self = None;

And put can be implemented in terms of get_or_insert (although it will give the optimizer a harder time)