Gaps in Rust memory-management feature space

The Shifgrethor garbage collection blog series and discussion have had me thinking about use cases where Rust’s existing memory management tools (language, standard libraries, and popular crates) fall short, or aren’t ergonomic.

Full garbage collection as a library is a useful, but big, hammer; as the blog post above puts it, Shifgrethor

is not simpler or more convenient to use than Rust’s normal APIs, for a variety of reasons. Its unlikely, then, that this project will result in garbage collection becoming a commonly used feature by Rust projects.

Instead, idiomatic Rust divides memory usage into different common patterns, and implements them with various smart pointers and so on. On top of that, crates have sprung up to address patterns such as making a reference or data structure own its referenced value (owning_ref) and designing self-referential structs (rental).

That gets me to my question:

TL;DR: what are some patterns you’ve found in practice where Rust’s existing memory management tools (standard library smart pointers, popular crates) are not enough to implement those patterns ergonomically? This relates to, but AFAICT is more specific than, the discussion about the “Rust Patterns” blog series.

To start things off:

  • An early pattern that gave me trouble is creating an iterator over the contents of certain smart pointers. The “Rust Patterns” series above explores some workarounds, but they involve non-obvious boilerplate or must be tailored to the specific contained type.
  • To generalize the pattern above: it can be frustrating to use types that were designed to hold onto bare Rust references / PhantomData – e.g., the return value of vec.iter() or vec.drain() – beyond a chosen lexical scope, by holding onto them in structs that move or live in the heap and doing something to extend the lifetime of what they refer to. (owning_ref applies here, but I think there’s room to explore the ergonomics further.)
2 Likes

Regarding strictly memory management, I really don’t find many problems. In complex cases I just add Rc/Arc, shrug, and move on.

Typing Rc::new(RefCell::new())) is annoying, so some syntax sugar for it would be nice, but to my surprise it hasn’t been a performance problem yet.

There are a couple of smaller things where borrow checker gets in the way:

In C I quite often I used a pattern similar to drain_filter. I could iterate over a whole array shifting elements back, and edit/move/remove elements at will in On(n) cost. Rust has only unstable method for it, and it’s unstable, because it’s not as flexible as the equivalent C loop.

Overlapping copy of a Vec. Non-overlapping in Rust is easy and fast, but overlapping copy requires unsafe.

3 Likes

I find working with atomic references (like the ones required to implement a concurrent queue) to be essentially uncovered by Rust memory management. That’s why the folks at crossbeam had to write a mini-GC just for this use case.