]> git.ralfj.de Git - web.git/commitdiff
memory safety: minor wording improvements
authorRalf Jung <post@ralfj.de>
Fri, 31 Oct 2025 20:13:57 +0000 (21:13 +0100)
committerRalf Jung <post@ralfj.de>
Fri, 31 Oct 2025 20:13:57 +0000 (21:13 +0100)
personal/_posts/2025-07-24-memory-safety.md

index e05d0a3c0ef50caeb8881a6f1156575299adae75..50c5db1d59ab62b382a74caa0213b56f8e7d0b50 100644 (file)
@@ -85,14 +85,14 @@ One could easily turn this example into a function that casts an integer to a po
 
 At this point you might be wondering, isn't this a problem in many languages?
 Doesn't Java also allow data races?
 
 At this point you might be wondering, isn't this a problem in many languages?
 Doesn't Java also allow data races?
-And yes, Java does allow data races, but the Java developers spent a lot of effort to ensure that even programs with data races remain entirely well-defined.
+And yes, Java does allow data races, but the Java developers spent a lot of effort to ensure that even programs with data races remain entirely well-defined and memory safe.
 They even developed the [first industrially deployed concurrency memory model](https://en.wikipedia.org/wiki/Java_memory_model) for this purpose, many years before the C++11 memory model.
 The result of all of this work is that in a concurrent Java program, you might see unexpected outdated values for certain variables, such as a null pointer where you expected the reference to be properly initialized, but you will *never* be able to actually break the language and dereference an invalid dangling pointer and segfault at address `0x2a`.
 In that sense, all Java programs are thread-safe.[^java-safe]
 
 [^java-safe]: Java programmers will sometimes use the terms "thread safe" and "memory safe" differently than C++ or Rust programmers would. From a Rust perspective, Java programs are memory- and thread-safe by construction. Java programmers take that so much for granted that they use the same term to refer to stronger properties, such as not having "unintended" data races or not having null pointer exceptions. However, such bugs cannot cause segfaults from invalid pointer uses, so these kinds of issues are qualitatively very different from the memory safety violation in my Go example. For the purpose of this blog post, I am using the low-level Rust and C++ meaning of these terms.
 
 They even developed the [first industrially deployed concurrency memory model](https://en.wikipedia.org/wiki/Java_memory_model) for this purpose, many years before the C++11 memory model.
 The result of all of this work is that in a concurrent Java program, you might see unexpected outdated values for certain variables, such as a null pointer where you expected the reference to be properly initialized, but you will *never* be able to actually break the language and dereference an invalid dangling pointer and segfault at address `0x2a`.
 In that sense, all Java programs are thread-safe.[^java-safe]
 
 [^java-safe]: Java programmers will sometimes use the terms "thread safe" and "memory safe" differently than C++ or Rust programmers would. From a Rust perspective, Java programs are memory- and thread-safe by construction. Java programmers take that so much for granted that they use the same term to refer to stronger properties, such as not having "unintended" data races or not having null pointer exceptions. However, such bugs cannot cause segfaults from invalid pointer uses, so these kinds of issues are qualitatively very different from the memory safety violation in my Go example. For the purpose of this blog post, I am using the low-level Rust and C++ meaning of these terms.
 
-Generally, there are two options a language can pursue to ensure that concurrency does not break basic invariants:
+Generally, there are two options a language can pursue to ensure that concurrency does not break memory safety:
 - Ensure that arbitrary concurrent programs still uphold the typing discipline and key language invariants. This comes at a significant cost, restricting the language to never assume consistency of multi-word values and limiting which optimizations the compiler can perform. This is the route most languages take, from Java to C#, OCaml, JavaScript, and WebAssembly.[^multi-word]
 - Have a strong enough type system to fully rule out data races on most accesses, and pay the cost of having to safely deal with races for only a small subset of memory accesses. This is the approach that Rust first brought into practice, and that Swift is now also adopting with their ["strict concurrency"](https://developer.apple.com/documentation/swift/adoptingswift6).
 
 - Ensure that arbitrary concurrent programs still uphold the typing discipline and key language invariants. This comes at a significant cost, restricting the language to never assume consistency of multi-word values and limiting which optimizations the compiler can perform. This is the route most languages take, from Java to C#, OCaml, JavaScript, and WebAssembly.[^multi-word]
 - Have a strong enough type system to fully rule out data races on most accesses, and pay the cost of having to safely deal with races for only a small subset of memory accesses. This is the approach that Rust first brought into practice, and that Swift is now also adopting with their ["strict concurrency"](https://developer.apple.com/documentation/swift/adoptingswift6).