Others / shredder

Garbage collected smart pointers for Rust
MIT License
266 stars 9 forks source link

Allow situational `Deref` on `Gc<T>` #32

Closed Others closed 3 years ago

Others commented 4 years ago

If the data inside the Gc<T> has no interior mutability (UnsafeCell) (except for inside other Gcs), then we can Deref directly to the data inside. This is both a performance and an ergonomics win.

We'd want to create another trait, with some name like NoUnguardedInteriorMutablity or ImmutableDirectly, and make people derive it if they want Deref for their Gc<T>.

Avi-D-coder commented 4 years ago

Take a look at https://github.com/rust-lang/rfcs/pull/2944. It's not sufficient for my GC's use case, but it might be for yours.

Others commented 4 years ago

@Avi-D-coder actually reading that was the impetus for me to create this issue! I think Freeze isn't good enough for me either, since Box<RefCell<T>> is not Freeze. (And what I really need to capture is that there is no interior mutability outside of a Gc.)

mtak- commented 4 years ago

Box<RefCell<T>> would be Freeze, since !Freeze is not transitive across pointers.

Others commented 4 years ago

@mtak- That doesn’t seem to match RFC?

My type doesn't implement Freeze, but I need it to be Freeze. To convert a type which is not Freeze, into a Freeze type, all that is required is to stick it on the heap. For example, Box is Freeze even if T is an UnsafeCell.

mtak- commented 4 years ago

To translate those sentences to your specific type: RefCell<T> is not Freeze, but Box<RefCell<T>> is Freeze.

playground

EDIT: Others have noted those lines are a bit confusing. So I'll take this issue into account while rewriting it.

Others commented 4 years ago

@mtak- my mistake, I misspoke in my original comment. The problem with Freeze for shredder is exactly that Box is Freeze. shredder cares about interior mutability, irrespective of if it’s across a pointer.

mtak- commented 4 years ago

Ah. So you want deep immutability. Do you mind explaining why shallow immutability isn't enough? I do not know much about how shredder works.

Others commented 4 years ago

@mtak- During certain parts of collection I can’t allow certain gc objects to be concurrently modified. That’s why I require guarded access.

However if data is deeply immutable (and Sync), it’s okay to have a Deref implementation. (Since I only care if the data is changed.)

Furthermore each Gc is treated separately, so shredder could also allow Deref if you use Gc to wrap your mutable data.

Honestly Freeze is just not capturing what I need. That’s why (as I mentioned in the RFC thread), I want opt-in user defined auto traits in some form. (Although I might be able to emulate this in my Scan derive.)

(Wrote this on my phone—apologies for any typos)

Others commented 4 years ago

I think the path forward for this is to introduce another couple of traits.

We need the trait to guarantee the deref constraint--that the data is sync and lacks interior mutability.

// Allowed only when the object is deeply immutable 
unsafe trait GcDeref: Sync {}

We need a smart pointer that allows deref:

struct DerefGc<T: GcDeref + Scan> 

Secondly, we need a trait to represent that the data can have a safe destructor (we can't have a destructor DerefGcs! That could lead to undefined behavior in the presence of data cycles. )

// Allowed only when we can access no `DerefGc<T>`
unsafe trait SafeToDestruct {}

I think this design is workable, but may only be possible on nightly--where we can set up auto traits. It'd be nice if our derive macro could somehow impl these traits when appropriate, but I think SafeToDestruct in particular requires negative reasoning.

Also note that if we implement this, we are losing the ability to have moving collectors in the future.

Others commented 3 years ago

Resolved on master