That's an interesting question, and there has been some work in that area.
Getting shared mutability as a user of GC is basically the same as getting shared mutability in any other scenario in Rust. You use `UnsafeCell<T>` or a safe wrapper around it, like `Mutex` or `Cell`. You can get a feel for this even in idiomatic Rust using `Rc` instead of a tracing GC.
When you start looking at other methods of GC things get trickier. Even with `UnsafeCell<T>` the GC can't, in general, know whether something is borrowed (mutably or immutably) by the mutator, so the problem to be solved is finding ways to enforce that there are no references into the heap at all, when collection needs to happen.
But when you think about it, this is actually not so different from the usual problem of GC safepoints. The problem is the same- enforce that there are no un-rooted pointers into the heap, when collection needs to happen.
And it turns out there are some tricks you can do with lifetimes to get borrowck to enforce safepoints for you, and it could even be made pretty ergonomic in the future.
I think gc-arena is fairly similar to josephine- both treat the GC heap itself as a container-like object, requiring `&` or `&mut` access to dereference a GC pointer. Gc-arena also suggests that we could use generator yield points as GC safepoints- they have exactly the required lifetime properties. So you might (to borrow Python syntax) `yield from fn_that_allocates_gc_memory()`, with the compiler ensuring you don't hold any references into the heap across that call.
Both gc-arena and josephine appear to take the "unidirectional" approach, where there is no recursive re-entrance into the GC heap. For example gc-arena will not collect while the heap is being mutated, which is Rust-friendly but also impractical.
GC'd languages typically require reentrancy into managed code (managed -> native -> managed), and this has historically been the source of type confusion and other security vulnerabilities.
> gc-arena will not collect while the heap is being mutated, which is Rust-friendly but also impractical.
This is where the generator stuff I mentioned comes in- I think the approach is much more practical than it seems.
At the end of the day, any GC language needs to ensure there are no live un-rooted GC pointers at points where collection takes place. So any realistic Rust/GC integration will need to solve the same problem, if it is to retain memory safety.
And the best way (so far) to ensure all references into the heap have disappeared is to make sure they are all derived from a function parameter with an unconstrained lifetime, because then the mutator knows nothing except that it might go away when it returns. (This is "generative lifetimes" from the links.)
The real trick then is to make it look like the mutator is returning, without actually forcing it to exit. A yield point in a generator can do this, even without ever actually yielding. So you can manually insert collection points in the middle of a Rust mutator, and you can have a "managed -> native -> managed" stack where the native frame thinks its managed callee can yield across it, and borrowck will ensure anything derived from that reference parameter is gone.
Of course this is a long way off from Rust today, where generators aren't available on stable (though perhaps you could hack something together with async/await), but does look like it should eventually be possible to have reentrant heap access and memory safety across Rust and a GC language.
Getting shared mutability as a user of GC is basically the same as getting shared mutability in any other scenario in Rust. You use `UnsafeCell<T>` or a safe wrapper around it, like `Mutex` or `Cell`. You can get a feel for this even in idiomatic Rust using `Rc` instead of a tracing GC.
When you start looking at other methods of GC things get trickier. Even with `UnsafeCell<T>` the GC can't, in general, know whether something is borrowed (mutably or immutably) by the mutator, so the problem to be solved is finding ways to enforce that there are no references into the heap at all, when collection needs to happen.
But when you think about it, this is actually not so different from the usual problem of GC safepoints. The problem is the same- enforce that there are no un-rooted pointers into the heap, when collection needs to happen.
And it turns out there are some tricks you can do with lifetimes to get borrowck to enforce safepoints for you, and it could even be made pretty ergonomic in the future.
Some early discussion of the problem space:
* http://blog.pnkfx.org/blog/categories/gc/
* https://manishearth.github.io/blog/2015/09/01/designing-a-gc...
* https://manishearth.github.io/blog/2016/08/18/gc-support-in-...
A design from Servo, and a paper to go with it: https://github.com/asajeffrey/josephine
A series of blogposts describing a more recent attempt: https://boats.gitlab.io/blog/post/shifgrethor-i/
The most recent attempt I know of: https://github.com/kyren/gc-arena/
I think gc-arena is fairly similar to josephine- both treat the GC heap itself as a container-like object, requiring `&` or `&mut` access to dereference a GC pointer. Gc-arena also suggests that we could use generator yield points as GC safepoints- they have exactly the required lifetime properties. So you might (to borrow Python syntax) `yield from fn_that_allocates_gc_memory()`, with the compiler ensuring you don't hold any references into the heap across that call.