Returning an reference with the referent

Hi all!

I'm enjoying Rust a lot! Coming from Python, I've had plenty of trouble with generics, but I've just run into my first real fight with the borrow checker!

I'm coding a molecular simulation engine in Rust as a toy project. I have a struct that performs an expensive calculation that I want to compute only occasionally. I want to be able to share a single instance of this cache struct among multiple instances of struct that use the expensive calculation. For convenience, I want to be able to produce both the cache struct and a struct that uses it with a single new() method, something like this:

fn new() -> (Self, Cache) {
    let cache = Cache::new();
    (
        Self { cache: &cache},
        cache
    )
}

This seems like a reasonable thing to do - it just passes responsibility to the calling scope. But it doesn't compile; the compiler complains both that we can't return a reference when the referent goes out of scope, AND that we can't return something while its being borrowed.

More complete example in playground here.

I can obviously pretty easily work around this, but it means I can never make a convenience function that creates both the cache and the using struct, which seems restrictive. Is there a workaround for returning a reference with the referent? Is this a compiler oversight or am I missing something? Are there patterns for doing this sort of thing? Should I go figure out how macros work?

Thanks!

I think this helps:

1 Like

The easiest thing here is to probably return a Rc<Cache> and just keep Rc<Cache> wherever you need a shared reference to the cache.

In your playground example, it looks like you create NeedsACache with an uncomputed Cache, and only later do you do the expensive computation. This requires a &mut Cache, and Rc won't like that. You may need to add interior mutability to it, something like this:

struct Cache {
    value: Cell<Option<f64>>,
}

struct NeedsACache {
    cache: Rc<Cache>,
}

(... or RefCell instead of Cell if your answer doesn't implement the Copy trait.)

3 Likes

The actual error is that you're trying to return a dangling pointer to a stack variable, a classic error that Rust was designed to prevent! Once you return from NeedsACache::new, &cache points to an invalid stack address, which Rust rightly rejects. If you wanna do something smarter with pointers, you need to turn to... smart pointers to heap allocations like Box, Rc, Arc, etc. as others have suggested.

1 Like

Thank you all! I had figured out the bit about the stack, but when boxing Cache didn't fix it I wrongly concluded that wasn't the problem. @hellow, that post was super helpful for showing that there was a compiler over-strictness concern there and the links to crates that work around that. @federicomenaquintero, thanks for pointing out that mutability issue. I've decided to re-organise my library so that NeedsACache is passed &Cache as an argument when it needs it, which (I think) alleviates both concerns.

Thanks so much for your help!