> I'm trying to figure out where our understandings differ, since that generally sounds familiar and reasonable to me. I guess you're assuming the callback/accounting code needs to be unsafe or able to violate the GC's preconditions? But I don't see why you assume that. As part of the GC, build some data structures that can handle that accounting and present a safe API, then use those data structures anywhere you don't want to create a root. When the browser code accesses the contents, barriers can be applied automatically.
If this code was all written in Rust, I could imagine there being almost no uses of `unsafe`, except for one: the thing where the GC decides to delete an object.
But this means that all of that code that isn't marked `unsafe`, but instructs the GC about what objects to mark or not, is really super unsafe because if it makes a logic error in telling the GC what to mark then the GC will delete an object that it should not have deleted.
So, the problem here isn't that you can't wrap the unsafe stuff in a safe API. The problem is that even if you do that, all of your seemingly-safe code is really super unsafe.
> The hard part of using a data structure like that is giving it ownership of the data and control over destroying it, but Rc does that too, doesn't it? That kind of thing is why I mentioned Rc. The difference between Rc and GC is much more in the behind-the-scenes tracking than in the API it gives.
The difference between RC and GC is exactly in the fact that the API they give is radically different. And that the behind-the-scenes tracking is different, too.
It's totally valid to tell RC "I want to point at this object so keep it alive".
But that's not the API that the GC will give you, unless it's a pinning API. The API where you tell the GC "keep this alive" will prevent the deletion of the garbage cycles I mentioned earlier.
So, the GC (in the case of something like a browser) gives a different API: one where user code either marks something, or not, at its discretion. There's no easy way to make that safe.
> But this means that all of that code that isn't marked `unsafe`, but instructs the GC about what objects to mark or not, is really super unsafe because if it makes a logic error in telling the GC what to mark then the GC will delete an object that it should not have deleted.
By putting it in a GC structure, it would be giving ownership to the GC, and would borrow it back to use. So whenever it's not borrowed, it's safe for the GC to delete it. And the GC won't delete anything outside of its control.
If you have a logic error and prematurely delete, then attempting to borrow the object will return None, which is still safe.
> It's totally valid to tell RC "I want to point at this object so keep it alive".
> But that's not the API that the GC will give you, unless it's a pinning API. The API where you tell the GC "keep this alive" will prevent the deletion of the garbage cycles I mentioned earlier.
The idea is that any pointing/pinning you do while manipulating an object would be a borrow on the stack. As soon as the function returns, there's no pinning.
So there's a few things that exist in this system:
* A GC-object references another GC-object, keeping the target alive while it is alive. A GC-object can be created by either Rust or JS. The loop you describe would be made up of GC-objects, so it would be straightforward to collect.
* A non-GC object has a permanent pinning reference to a GC-object. These are rare and purposeful.
* (Optional) A non-GC object has a weak reference to a GC-object, which can be collected at any time.
* Rust code has a temporary pinning reference to a GC-object on the stack, while traversing/manipulating it, and it goes away as soon as the function exits. It won't pin too long because the release is automatic. It won't free prematurely because the GC code knows the borrow is happening.
If this code was all written in Rust, I could imagine there being almost no uses of `unsafe`, except for one: the thing where the GC decides to delete an object.
But this means that all of that code that isn't marked `unsafe`, but instructs the GC about what objects to mark or not, is really super unsafe because if it makes a logic error in telling the GC what to mark then the GC will delete an object that it should not have deleted.
So, the problem here isn't that you can't wrap the unsafe stuff in a safe API. The problem is that even if you do that, all of your seemingly-safe code is really super unsafe.
> The hard part of using a data structure like that is giving it ownership of the data and control over destroying it, but Rc does that too, doesn't it? That kind of thing is why I mentioned Rc. The difference between Rc and GC is much more in the behind-the-scenes tracking than in the API it gives.
The difference between RC and GC is exactly in the fact that the API they give is radically different. And that the behind-the-scenes tracking is different, too.
It's totally valid to tell RC "I want to point at this object so keep it alive".
But that's not the API that the GC will give you, unless it's a pinning API. The API where you tell the GC "keep this alive" will prevent the deletion of the garbage cycles I mentioned earlier.
So, the GC (in the case of something like a browser) gives a different API: one where user code either marks something, or not, at its discretion. There's no easy way to make that safe.