Aliasing model and pointers becoming references

As I understand it using pointers instead of references helps you work around lifetime problems but not &mut aliasing problems. The reason being that when you dereference a pointer what you produce is a reference and then the aliasing rules for references apply. When I write unsafe code this is one of the hardest things in practice to account for and seems easy to get wrong. Compared to avoiding lifetime problems needing to reason about aliasing when designing safe wrappers around unsafe code seems much harder and seems to more often require non-local reasoning.

Was there any design consideration given to making it so the dereferencing a pointer does not produce a regular reference? Not sure what to search for to find this kind of discussion. I could imagine there being three types, pointers, references, and "unsafe references" (can't think of a better name). Pointers when dereferenced would produce "unsafe references" that the compiler would not be able to make any aliasing assumptions about. This means a function that took two regular references and a function that took two unsafe references would have different codegen. It would still be UB for a mutable regular reference and unsafe references to the same object to coexist but multiple unsafe references would be defined.

I think many people misunderstand the aliasing rules to be a lot stricter than they really are. A &mut T can alias with the thing it was created from, but all copies of the same raw pointer are considered the same thing, so a &mut T may also alias with copies of the raw pointer it was created from. (This was also discussed in an earlier thread of yours).

You don't really run into problems until you try to store a raw pointer that you got from a reference, which you don't have to unless you are dealing with intrusive things (i.e. things that are self-referential and on the stack).

Now that I look closer, I guess I'm saying mostly the same stuff as what I said in your last thread.

This question is a bit too abstract to understand what kind of problems you're trying to solve. Maybe you could try providing a concrete code example where creating a reference is problematic and some special new type of reference/pointer is supposed to help.

Note that dereferencing a pointer does not create a reference. Instead it gives you a place expression - you then can take a reference to that place expression (either manually explicitly, or implicitly with a method call). You can also assign to the place, or copy the value, use it to create more complex place expressions to e. g. some fields, and/or use ptr::addr_of to create new pointers.

1 Like

To address your post a bit more directly, you are worried about uses of your raw pointers being temporarily turned into real references, and the existence of that temporary reference having bad aliasing consequences on the rest of your code. You propose introducing some kind of unsafe reference type without aliasing guarantees. However, to explain why this is unnecessary, I would like to argue that operations that don't involve real references at all (e.g. std::ptr::write) and operations that temporarily construct a mutable reference, immediately use it, then throw it away, have the exact same consequences aliasing-wise:

Let's say that we have a long lived raw pointer p, and that the temporary mutable reference is called m. We consider the operation's effect on other references of each type:

  1. What effect does the operation have on copies of p? Well since copies of p are considered parents of m, they are not invalidated by the creation or use of m and they continue to be valid. Of course, any use of those copies of p before the last use of m would not be valid, but we assumed that the mutable reference was used and then thrown away immediately, which I did precisely to exclude this case.
  2. What effect does the operation have on other mutable references that were created from p before our operation? Well, the aliasing rules applied to that previous mutable reference say that the old mutable reference is invalidated as soon as you use p, and it doesn't matter whether the use of p is an std::ptr::write or the creation of m.
  3. What effect does the operation have on other immutable references that were created from p before our operation? Well, the aliasing rules for immutable references require that the value is unchanged between any two uses of the immutable reference, so either kind of write would invalidate the immutable reference going forward.
  4. What effect does the operation have on raw pointers that are derived indirectly from p through a reference? Well, the things that invalidate the derived raw pointer are the same as what would invalidate the reference it was derived through, and we already covered references.

A similar argument can be given for read operations that use either std::ptr::read or go through a short-lived temporary immutable reference.

So to conclude: The access with the temporary reference and the one without a temporary reference behave in exactly the same way aliasing-wise.

Note: "copies of p" here includes operations such as pointer::add and casts. Only operations that go through a reference yields something that is not considered a copy of p.


This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.