Handling reference cycles with Drop instead of Weak

Per the book:

Rust’s memory safety guarantees make it difficult, but not impossible, to accidentally create memory that is never cleaned up (known as a memory leak ).

IMO cycles are probably not accidents, they are quite likely intentional, assuming the Rustacean is not having a bad day :slight_smile:

My idea is instead of using Weak references, by implementing Drop any potential cycles can be broken by (for example) clearing any interior values which may have created a cycle, typically clearing some Vecs.

In my case I have named (potentially recursive) routines in my interpreted database language, which compile to lists of instructions (ilist), and the Drop implementation looks like this:

impl Drop for Database
{
  /// Clear routine instructions to avoid leaking memory.
  fn drop(&mut self) 
  { 
    for routine in self.routines.borrow().values()
    {
      routine.ilist.borrow_mut().clear();
    }
  }
}

Here routines is a RefCell HashMap and ilist is a RefCell Vec ( I hope that's enough context to explain the code ).

I think it's a bit simpler than using Weak references, and potentially more efficient, although I haven't tried using Weak references myself yet. Anyone else had this same idea? Is it valid? Comments?

I guess if it works, then it’s valid. Assuming that you tested there are actually no leaks when cycles are created.

No, not really IMO. Really, this amount of code is so little that it’s kind-of impossible to understand the details of your approach in detail. It’s really not a “code review” (your choice of category) like this at all; even if this ways a “help” post, I’d like to ask for more details :wink:


Well, doing some guesswork, I assume you’re talking about some kind of datastructure Database that contains some potentially cyclic thing with Rcs inside of it somehow; then use the drop implementation of the enclosing structure to unlink the connections thus removing the cycles. In principle this can work. It’s hard to know whether it’s still possible to (accidentally) use your datastructure’s API to accidentally leak some cyclic Rcs after-all, if you aren’t showing any API.

Creating (potentially) cyclic structures that have a clear outer owning struct can also sometimes be accomplished nicely using some form of arena instead.

3 Likes

Right, to avoid surprising results from "destroying" the routines, it has to be the case that the cyclic things are contained. In my case, in order to execute a routine, you need a reference to the Database, so there is no way a valid attempt can be made to execute a routine after the Database has been dropped ( in fact execution is entirely internal, with my current API, although that might change ). It's roughly speaking "Execute this string".

I have no idea what an arena is yet...

The kinds of data structures offered in crates like the following

https://crates.io/keywords/arena

One could say that a Vec<T> is also a form of arena. You can allocate things in an arena (like Vec::push), then get some form of handle back to the allocated object (the index of the value in the case of Vec). Some arenas also support deallocating things through that handle (Vec doesn’t). They can be useful when you want to do kind-of manual memory management in a safe way. Indices into arenas are similar to raw pointers, but safe; access usually happens through the arena. (Like Vec::get(index)/Vec::get_mut(index)). And a big advantage is that you cannot leak anything. At the end of the day, dropping the whole arena will drop all the contents. They’re particularly useful and easy to use if you never want to drop any items of the arena prematurely anyways.

Different crates offer different flavors of arena for different use cases.

1 Like

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.