Jake has already mentioned how the type system enforces single-threading (using similar mechanisms as Rc in Rust), but it's not necessarily the case that read-write races from multiple threads are UB.
In particular, Java allows aggressive multithreaded access, and the memory model has some pretty strong guarantees. Informally stated, if a read and a write race, then the read is guaranteed to observe either the old or new value.
Go is something of a middle ground [1]. Races on simple scalar values are not UB, but "fat pointers" (slices and interfaces) can tear and can lead to "arbitrary memory corruption."
When I was reading about "stable shape" I was wondering if it might be something similar. You easily get the same UB problems when dealing with sum types, as a tear across tag and variant payload can cause all kinds of things to go wrong.
In particular, Java allows aggressive multithreaded access, and the memory model has some pretty strong guarantees. Informally stated, if a read and a write race, then the read is guaranteed to observe either the old or new value.
Go is something of a middle ground [1]. Races on simple scalar values are not UB, but "fat pointers" (slices and interfaces) can tear and can lead to "arbitrary memory corruption."
When I was reading about "stable shape" I was wondering if it might be something similar. You easily get the same UB problems when dealing with sum types, as a tear across tag and variant payload can cause all kinds of things to go wrong.
[1]: https://go.dev/ref/mem