Search code examples
asynchronousrustoption-typeborrow-checker

How to asynchronously memoize a field of struct in an Option


Suppose I have some data Bar (e.g. database client) which I would like to create only once but lazily for my structure Foo.

struct Bar;

struct Foo {
    bar: Option<Bar>
}

To do this, I check that the field is initialized; if not, I run the async routine. The result of the routine is then saved as Some to reuse later.

I know, that Option::get_or_insert_with perfectly fits this scenario, but I have to deal with async, so I do this manually like this.

impl Foo {
    pub async fn get_bar(&mut self) -> &Bar {
        if let Some(bar) = &self.bar {
            return bar;
        }
        
        let bar = Self::create_bar().await;
        self.bar.insert(bar)
    }
    
    /// Long and heavy-resource routine,
    /// we want to memoize it.
    async fn create_bar() -> Bar {
        Bar
    }
}

However, this cannot be compiled due to the immutable and mutable borrowing of self.bar. Is there a way to do this correctly?

Full example.


Solution

  • Interestingly the borrow checker is able to infer better lifetimes by using the ref keyword in your if let so the following works:

        pub async fn get_bar(&mut self) -> &Bar {
            if let Some(ref bar) = self.bar {
                return bar;
            }
            let bar = Self::create_bar().await;
            self.bar.insert(bar)
        }