News for hackers. Stuff that matters.
Re:The Hare programming language by nyanpasu64 on Monday April 25, 2022 @05:33AM (#31152245)
Not staticassertion, but I'm a hobbyist in real-time audio. I like Rust as a vocabulary for describing/teaching safe programming (&/&mut/Send/Sync). I find that multithreaded programs written in Rust are usually correct while multithreaded programs written in C++ are usually wrong, because Rust encodes the rules of shared-memory threading in its type system (&T: Sync objects are thread-shared, but are either immutable or atomic or requires locking to acquire a &mut T). I also appreciate guiding users towards exclusive references (&mut) to make it easier to reason about code. However I find it makes it too difficult to mutate through shared references or write unsafe code (passing Stacked Borrows while lending out &mut is more like solving puzzles than writing imperative code, and writing code that never touches & and &mut is a quagmire of addr_of_mut!() and unsafe blocks on every pointer dereference), and the Rust community appears uninterested in making unsafe programming more ergonomic.

Personally I'm a fan of C++'s unique_ptr/& as an unsafe escape hatch from Rust's single ownership or runtime refcounting overhead. It's at least as safe as Rust's unsafe pointers, and far more pleasant to use. Qt's QObject ownership system is reasonably ergonomic and QPointer is fun (though dangerous since it can turn into null unexpectedly), but Qt uses it pervasively (rather than only when safe memory management fails), relies on prose documentation to describe which pointers transfer ownership or not (resulting in memory management bugs), and QObject child destruction and nulling-out QPointers relies on runtime overhead. I haven't tried ECS or generational indexes yet, but those are popular in games, and Vale has its own ideas in this field (https://verdagon.dev/blog/generational-references).

On an aesthetic/principled level, I'd rather punt alias analysis to the programmer (pointer/restrict or &/&mut) rather than compiler complexity/magic (TBAA and provenance checking). Glancing at https://harelang.org/specification/, it seems Hare lacks an equivalent of restrict/&mut, and I wonder if that prevents the compiler from ever adding support for removing redundant loads/stores through pointers.

Re:The Hare programming language by astrange on Monday April 25, 2022 @07:51PM (#31162368)
> On an aesthetic/principled level, I'd rather punt alias analysis to the programmer (pointer/restrict or &/&mut) rather than compiler complexity/magic (TBAA and provenance checking).

That would certainly be nice, but the state of the art on what problems even are is far ahead in optimizing compilers than anyone else - "having a restrict keyword" doesn't solve every aliasing problem afaik, and nobody respects the performance people when they tell you undefined behavior in C is actually useful. So nobody has come up with a simple solution for a better language that solves problems like pointer provenance and yet is "faster than C".

Actually most people's ideas of how to make programs faster are complicated things like autovectorization that don't work and would make it slower.

Re:The Hare programming language by zozbot234 on Monday April 25, 2022 @05:39AM (#31152285)
> However I find it makes it too difficult to mutate through shared references

It's not that difficult, you just need to use UnsafeCell<…> or one of its safe derivatives (each of which has some potential runtime overhead) to keep the semantics tractable.

Re:The Hare programming language by nyanpasu64 on Monday April 25, 2022 @05:51AM (#31152377)
One of the strange things about Rust is the &UnsafeCell<T>/*mut T dichotomy. &UnsafeCell<T> is easier to work with, and you can soundly acquire &mut T as long as they never overlap, but you can't turn a Box<UnsafeCell<T>> into a &UnsafeCell<T> and back to a Box<UnsafeCell<T>> to delete it, because provenance or something.

*mut T is harder to work with, this is UB according to miri since you didn't specify `&mut x as *mut i32 as *const i32`:

    let mut x = 1;
    let px = &mut x as *const i32;
    unsafe {
        *(px as *mut i32) = 2;
    }
Problem is, most APIs won't give you a &UnsafeCell<T> but rather a &mut T. Not sure if you can convert a &mut T to a &UnsafeCell<T> (you definitely can't using `as`). If you want to create multiple aliasing pointers into a non-UnsafeCell type or struct field, one approach (basically a placeholder since &raw isn't stable, https://gankra.github.io/blah/fix-rust-pointers/#offsets-and...) is:

    let mut x = 1;
    let px = addr_of_mut!(x);
    unsafe {
        *px = 2;
    }
Re:The Hare programming language by kzrdude on Monday May 02, 2022 @03:03PM (#31239372)
Cell::from_mut converts from `&mut T` to `&Cell<T>` and that's a newtype around UnsafeCell - it has the same `as_ptr` method.
Re:The Hare programming language by yakubin on Monday April 25, 2022 @06:07AM (#31152498)
You cannot turn a &T into a Box<T>, because &T borrows T, while Box<T> owns T, and moreover it holds it in a separate allocation, so even &mut T cannot be transformed into Box<T> --- it already lives in some allocated space and whatever there is a reference to, cannot be moved to a new allocation. For moving T you need T, not a reference to T. The case with UnsafeCell<T> substituted in place of T is just a special case.

UnsafeCell<T> also owns T, so transforming &mut T into UnsafeCell<T> also doesn't make sense. The unsafe equivalent of references is pointers.

Re:The Hare programming language by nyanpasu64 on Monday April 25, 2022 @06:16AM (#31152554)
C++ lets you easily delete a T* or T const*, Rust has https://doc.rust-lang.org/std/primitive.pointer.html#2-consu... I guess?

> UnsafeCell<T> also owns T, so transforming &mut T into UnsafeCell<T> also doesn't make sense.

I wanted to transform a &mut T into &UnsafeCell<T> (note the &) and copy the reference, to allow shared mutation scoped within the lifetime of the source &mut T. How can this be accomplished?

Re:The Hare programming language by yakubin on Monday April 25, 2022 @06:47AM (#31152759)
> C++ lets you easily delete a T* or T const*, Rust has https://doc.rust-lang.org/std/primitive.pointer.html#2-consu... I guess?

Box deletes the owned object when it goes out of scope without being moved (like unique_ptr in C++). So if anything, you want to go the other way: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.f...

However, you can delete a *T by using <https://doc.rust-lang.org/std/alloc/trait.Allocator.html#tym...> with the Global allocator (since this is the one you're most likely using).

> I wanted to transform a &mut T into &UnsafeCell<T> (note the &) and copy the reference, to allow shared mutation scoped within the lifetime of the source &mut T. How can this be accomplished?

If you want to have two instances of one &mut T, you don't go through &UnsafeCell<T>. Instead you may cast &mut T into *mut T and then use this: <https://doc.rust-lang.org/std/primitive.pointer.html#method....>. This however will cast into any lifetime, so if you want to bind the two lifetimes together, then you need to have the lifetime of the original &mut T explicitly specified, and then you assign the result of the method I linked to a variable with explicitly specified type where you specify the lifetime annotation. Alternatively, you may write a separate function which accepts both references as arguments and binds the two lifetimes together the usual way.

I admit it's a bit unergonomic. The best way currently would be to have the data stored as UnsafeCell in the first place and then call get_mut() on it to get all the references. However, if this reference comes from outside, you're left with the little mess I outlined above.

Re:The Hare programming language by zozbot234 on Monday April 25, 2022 @06:02AM (#31152460)
These are different things. UnsafeCell<T> is for shared mutable data. *mut T is for data that you assert will never be mutated while it's being shared with some other reference, but you can't rely on the compiler to prove this fact for you.
Re:The Hare programming language by nyanpasu64 on Monday April 25, 2022 @06:16AM (#31152565)
If I have a &mut T, what pointer type do I convert it into (and create multiple copies of), to allow shared mutation scoped within the lifetime of the source &mut T?
Re:The Hare programming language by zozbot234 on Monday April 25, 2022 @06:35AM (#31152671)
You can have multiple *mut T aliasing to the same data, but if you mutate through them you're on your own wrt. Rust's safety rules.
Re:The Hare programming language by vladharbuz on Monday April 25, 2022 @05:44AM (#31152322)
Thank you for the resources, I'll look into these details in more depth!