News for hackers. Stuff that matters.
Re:The Next Steps for Single Ownership and RAII by Animats on Friday July 17, 2020 @12:57AM (#23867198)
I'm curious about that second safety hole Rust has.

All variables are supposed to be initialized before use. But a Rust Vec, like a C++ std::vector<T>, has both an in-use "size" and a usually-larger pre-allocated "capacity". This allows adding elements without getting a new memory block.

Implementing Vec safely in Rust runs into the problem of initializing the capacity not yet used. Not initializing it is unsafe, so Vec has to have "unsafe" code.

I'd argue for this:

First, data types should be divided into "fully mapped" and "not fully mapped". A fully mapped type is one where all bit combinations are valid. Fully mapped types don't have to be initialized to be memory-safe. This includes integers, floating point on most systems, but not booleans. (C++ calls this "plain old data", or POD, but allows Booleans and enums. Whether Booleans with values other than 0 or 1, or out of range enums, should be considered "plain old data" is a good question for a language designer.)

If it's not fully mapped (worst case, it has a pointer or reference in it), what do you do about uninitialized data in the unused "capacity" area? If it's not initialized, you have a pointer with a junk value, as far as the language is concerned. If it has to be initialized, you have a problem if initialization requires an input value.

So I'd argue for the concept of a partially initialized array as a language feature. For simplicity, only being initialized from the beginning to a limit (0..N) should be supported. (Yes, there is such a thing as a giant sparsely initialized hash table where allocation info is stored outside the table. See Google's sparse hash table. That's very rare.) The idea is that you designate some field of the structure as the "size", and accessing beyond "size-1" is a subscript error. But you can initialize at "size", and this increases "size" by 1. You can decrease "size" if you own the whole object.

If you have both of those features in a Rust-like language, you should not need "unsafe". Except to call other languages, maybe.

Re:The Next Steps for Single Ownership and RAII by algesten on Friday July 17, 2020 @02:47AM (#23867837)
I don't think you can have a "fully mapped type" the way you describe it. Uninitalized memory in today's compilers do not have an observable value.

https://www.ralfj.de/blog/2019/07/14/uninit.html

Re:The Next Steps for Single Ownership and RAII by twoodfin on Friday July 17, 2020 @10:10AM (#23870618)
The compiler’s representation of an uninitialized lvalue might not be observable as the article describes, but an uninitialized heap allocation from the OS (i.e. malloc()) is observable by design.

Of course a language and its clever implementation can make reading unintialized values from malloc()‘d objects UB with all that implies, but that’s a spec choice.

So I believe you could define what’s returned by malloc() as safely castable to one or more “fully mapped types”, which would solve the problem for vectors at least of those types.

Re:The Next Steps for Single Ownership and RAII by Animats on Friday July 17, 2020 @02:27PM (#23873781)
Yes, you need to be able to at least do that, for big I/O buffers and such. Those are usually raw arrays of bytes, so no problem there.

The problem I was discussing is with growable arrays, where the memory underneath is partially initialized.

Re:The Next Steps for Single Ownership and RAII by verdagon on Friday July 17, 2020 @01:44AM (#23867455)
That's interesting! I'm fairly certain Vale avoids the problem with its lambda approach, but I can see a partially initialized array being very useful for Rust. In fact, I think Cone [1] does exactly that; you can designate a certain field to be the size for another an array field.

I haven't looked at google's sparse hash table yet, I'll have to codesearch that =)

[1] https://cone.jondgoodwin.com/

Re:The Next Steps for Single Ownership and RAII by Animats on Friday July 17, 2020 @05:53PM (#23876354)
In fact, I think Cone does exactly that;

Interesting. Cone has every known approach to memory management. GC, refcounts, arenas, pools, single ownership, thread local... Worth an experiment, but you probably don't want to go there for a widely used language. Think of the library compatibility problems.

For locking purposes, there's an argument for thread-local, shared, and transferable objects. Transferable objects are what you put in a queue between two threads. They're single-thread and don't need locking when in use, but can be handed off to another thread.

It's useful if transferable objects are transferable by copy. This is useful for machines where not all memory is shared. We're headed there; cache management gets harder and harder as the number of cores starts to approach 3 digits. So think ahead here.