Seperating NonNull into NonNullMut and NonNullConst

Hi, NonNull is a very good way to add a bit more safety for unsafe rust. The problem with it is that it comes at a cost of mutability safety.

If you use NonNull you don't need to worry about checking for nullness but now you need to have a mind map of what is *mut and what is *const.

I propose to split NonNull into NonNullConst and NonNullMut such that NonNullConst won't have any function exposing a mutable pointer/reference. that way you can benefit from both nulnness safety and mutability safety.

1 Like

Note that there isn't a lot of difference between *const T and *mut T either, it's mostly the lint: What is the real difference between `*const T` and `*mut T` raw pointers?

What matters for mutability is pointer provenience: did you get it from &T, &mut T or &UnsafeCell<T>.

1 Like

I think the idea is that keeping them separate makes it easier to reason about provedence.

2 Likes

@matklad I understand that. but the whole point here is to have compile time safety. so you won't need to try and remember what should be const and what should be mut. for the same reason NonNull helps you not remember if you checked for nullness already.

You should have 1 check in your code and then a type that helps you not converting a const to mut accidentally. currently it's "easier" to get a mut reference out of NonNull then out of *const T (one function call vs at least one cast + dereference. which is easy to see that it's wrong)

I used to think that, but then I learned that the borrow checker does not treat them equally, and following that, Stacked Borrows also currently makes a difference. To be precise, casting a reference to *const vs *mut is not the same operation. But once the cast-to-raw happens, any further casts between the two kinds of raw pointers do not change anything, and using either kind of raw pointer behaves exactly the same. The only difference is in when the safe reference initially gets cast to a raw pointer.

8 Likes