Return reference to value moved into mutable reference

How do I idiomatically allocate a new String inside a function, use it to update a mutable reference, and then return a reference to the moved String?

Here is an explicit example. The following does not compile for multiple reasons (referencing local variable and borrowing a moved value):

// only for demonstration purposes
use std::time::Instant;

enum Container {
    Something(String),
    Nothing,
}

fn something_from_nothing<'a>(container: &'a mut Container) -> &'a str {
    match container {
        Container::Something(ref s) => s,
        Container::Nothing => {
            let new = format!("{:?}", Instant::now());
            *container = Container::Something(new);
            &new
        }
    }
}

By using unreachable!(), this can be fixed:

fn something_from_nothing<'a>(container: &'a mut Container) -> &'a str {
    match container {
        Container::Nothing => {
            let new = format!("{:?}", Instant::now());
            *container = Container::Something(new);
        }
        _ => {}
    };

    match container {
        Container::Something(ref contents) => contents,
        Container::Nothing => unreachable!(),
    }
}

Is there a good way to avoid the unreachable!()?

1 Like

Judging by std’s Option::get_or_insert implementation

impl<T> Option<T> {
    pub fn get_or_insert(&mut self, value: T) -> &mut T {
        if let None = *self {
            *self = Some(value);
        }

        // SAFETY: a `None` variant for `self` would have been replaced by a `Some`
        // variant in the code above.
        unsafe { self.as_mut().unwrap_unchecked() }
    }
}

there doesn’t appear to be any particularly nice way. Of course, using Option it as a primitive could be an option (no pun intended) so your Container could become a struct

// only for demonstration purposes
use std::time::Instant;

struct Container(Option<String>);

fn something_from_nothing<'a>(container: &'a mut Container) -> &'a str {
    container.0.get_or_insert_with(|| format!("{:?}", Instant::now()))
}
2 Likes

I see, thanks for the very useful pointer to Option!

Indeed it looks like even unwrap_unchecked() is internally using (the unsafe version of) unreachable!():

pub const unsafe fn unwrap_unchecked(self) -> T {
    debug_assert!(self.is_some());
    match self {
        Some(val) => val,
        // SAFETY: the safety contract must be upheld by the caller.
        None => unsafe { hint::unreachable_unchecked() },
    }
}

In my use-case Container is substantially more complex and potentially not easily modelled as Option but it's definitely a good thing to keep in mind!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.