Given that ref structs can now be generic arguments and cannot be boxed - you have more ways to enforce that no boxing occurs at compile-time. It is true that you have to roll your own collections, but even dispatching on interfaces by making them generic constraints (which is zero-cost) instead of boxing is a good start.
As for delete operator, 'dispose' works well enough. I have a toy native vector that I use for all sorts of one-off tasks:
// A is a shorthand for default allocator, a thin wrapper on top of malloc/realloc/free
// this allows for Zig-style allocator specialization
using var nums = (NVec<int, A>)[1, 2, 3, 4];
nums.Add(5);
...
// underlying pointer is freed at the end of the scope
It is very easy to implement and I assume C and C++ developers would feel right at home, except with better UX.
This retains full compatibility with the standard library through interfaces and being convertible to Span<T>, which almost everything accepts nowadays.
System-provided allocators are slower at small allocations than GC, but Jemalloc easily fixes that.
Okay, I see where you are coming from. This is a common ask, but it works against the principles that make generational GCs performant. You can't "delete" an object from the heap, because dead objects are not deallocated. Instead, live objects are preserved and moved to an older generation, with memory now occupied by only dead objects made available for subsequent allocations immediately.
In addition, objects that hold references to other objects internally would need an implementation that would allow to traverse and recursively free references in a statically understood way. This gets nasty quick since a List<T> can hold, let's say, strings, which may or may not have other locations referring to them. Memory safety goes out of the window for dubious performance wins (not even necessarily, since this is where GC has better throughput).
> Okay, I see where you are coming from. This is a common ask, but it works against the principles that make generational GCs performant.
In my comment I already suggested a context where GC can be turned off. I said: "It would be better if the GC can be turned off with a switch and just add a delete operator to manually free memory."
And that'd totally break down as soon as some underlying class does something you didn't expect. C++ RAII patterns and Rust's ownership systems are required for a very good reason (that the GC sidesteps but also makes all code dependent of), the NVec further up in the thread works because it's an explicit abstraction.
As for delete operator, 'dispose' works well enough. I have a toy native vector that I use for all sorts of one-off tasks:
It is very easy to implement and I assume C and C++ developers would feel right at home, except with better UX.This retains full compatibility with the standard library through interfaces and being convertible to Span<T>, which almost everything accepts nowadays.
System-provided allocators are slower at small allocations than GC, but Jemalloc easily fixes that.