Thoughts on unsafe pointers

So we are getting close to the point where we can start actually having unsafe pointers. So let's write down a bit of concrete stuff about them.


  • Make it possible for the average programmer to comprehend the rules without needing an entire summer class in compiler optimizations
  • Make it easy to produce correct code
  • Make it hard to produce OOCP/UB, even with incorrect code
  • Make it more convenient to write embedded, OS or super-high-perf code than using C


  • Absolutely zero overhead everywhere
  • Making everything More Safe through complicated type juggling/trait salad
  • Needing a proof engine in the compiler

My thoughts right now are a bit fragmentary, but there's a few kinds of unsafe pointer that have stood the test of time:

  • Raw pointers. These are "normal" typed pointers that refer to memory. Their relationship with borrows is not yet fully defined.
  • MMIO pointers. These are "volatile" pointers that refer to memory-mapped I/O areas. This means that reads are never elided or reordered, writes are never elided or reordered, and the reading/writing of specific sized types never results in reads/writes of different sizes. You can do all this with Rust's "volatile" pointer methods, or in C's "volatile" pointers, but it's a pain in the ass and last I checked the semantics were not as well defined as one would want. So basically they are there for low-level programming with memory-mapped I/O, or possibly other types of structures the compiler can't reason about, like CPU page tables. Why not just use "volatile" pointer semantics like Rust or C? I mean, why would we want to? In reality those things get used mainly for MMIO anyway, so why not make that the first-class citizen? Also, a pointer to an array or struct in MMIO memory is a common AF pattern for that sort of thing, and so having a pointer where its properties explicitly translate down to such a thing is handy, and easier to wrap in a safe interface.
  • Addresses. Basically untyped pointers. Slightly inspired by Modula-2, this is not necessarily a good idea but is one I want to investigate. In practice these are just void pointers, but I think there's some ergonomic improvements to be had: One, I have vivid memories of learning C and trying to figure out what the fuck void actually meant, so having an entirely separate type that is more clear. Relatedly, people trying to write unsafe code in Rust for the first time often think that a sensible "void pointer" type is *mut (), which doesn't work the way they expect because () is unsized; the most sensible way to do "pointer to untyped memory" in Rust is *mut u8, which feels kinda weird since u8 is a number type, not any type. An Address would always be a thin pointer, pointer arithmetic on it is always allowed, and there are no assumptions of provenance, alignment or anything else associated with it: those are things that are instated or checked when the Address is converted to a typed pointer. It may also make "integer the size of a pointer" a la uintptr or Rust's misdesigned usize easier to define: it is the size of an Address. Pointers are not just integers, so this gives us a place to say "this is an integer that can be turned into a pointer" and vice versa.
Assigned to
1 year, 28 days ago
5 months ago

~icefox referenced this from #56 1 year, 28 days ago

~icefox 1 year, 28 days ago

I feel a little bad about proliferating pointer types, but I was told "98% of people will never need to touch these types, so don't worry too much about feeling like they add extra complexity for people to understand." So in the end I feel that these serve my purpose: make Garnet a borrow-checked language for low-level programming.

~icefox 1 year, 28 days ago*

Ok I just reread the article I linked in the original post and it's the source of a lot of this thought, so I'ma throw it down here explicitly: https://www.ralfj.de/blog/2018/07/24/pointers-and-bytes.html

Honestly Raph's blog in general is amazing.

Register here or Log in to comment, or comment via email.