Skip to content

Instantly share code, notes, and snippets.

@benjones
Created November 16, 2021 23:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save benjones/fa015904eba3e2a7ce1a75b3f6506cc7 to your computer and use it in GitHub Desktop.
Save benjones/fa015904eba3e2a7ce1a75b3f6506cc7 to your computer and use it in GitHub Desktop.
Thoughts on safe GC-less slices in Dlang

Here's a few thoughts I had about slices, safety, and RC based on some discussions in the Dlang forums

Currently slices conflate 2 different ideas: a view of a range of memory, and ownership of a range of memory. I'd say it's sort of analogous to both std::vector and a pair of iterators in C++. I'm not sure there's a good way to do both things safely with a single type without GC.

An analogous situation in D is objects vs refs. D avoids the "iterator invalidation" problem by not allowing ref variables, only ref parameters. Because of that the lifetime of the object is guaranteed to outlive the ref to that object.

So, one way to potentially have a GC-less dynamic array would be to define 2 different types: an owning slice and a borrowing slice. You can store values of an owning slice, but borrowing slices can only be passed to functions.

That's sort of the solution that I think Paul Backus proposed where instead of returning a slice, the RCSlice type (or whatever it was) took a delegate to operate on the slice, which enforced that lifetime constraint. His idea is what prompted me to write this.

The syntax for that is obviously not great, but we might be able to do better by stealing from functional languages. Perhaps something like this:

let (borrowedSlice; owningSlice) in {
   //code
}

mean essentially the same as

owningSlice.apply( (borrowedSlice) => { //code} );

One way to implement this is to have owningSlice implement opLet (this is similar to (I think?) Andrei's opFunctionCall idea)

The safety provided here seems to align pretty well with what's in the @live implementation as it's mostly about passing scope stuff to functions.

Can it be implemented without horrendous breakage? Probably not. Ideally the existing [] could be used for both types of slice depending on context. Some thoughts on how to transition:

  • All slice variables become owning slices
  • Owning slice should have to be assigned from unique rvalues like from .dup or new
  • It's OK to shrink a borrowed slice, but attempting to grow one would make a new owning slice, I think

I originally thought this might be related to/helpful for an RC slice, but that's not really the same thing. In that case, I think you'd have multiple "owningslice-like" objects that are related and you'd still use opLet to work with them as necessary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment