1

I want to reinterpret an immutable reference to a mutable reference (in an unsafe block) and be responsible for the safety checks on my own, yet it appears I cannot use mem::transmute() to do so.

let map_of_vecs: HashMap<usize, Vec<_>> = ...;
let vec = map_of_vecs[2];
/// obtain a mutable reference to vec here
  • I do not want to wrap the Vecs into Cells because that would affect all other areas of code that use map_of_vecs and I only need mutability in one line.
  • I do not have mutable access to map_of_vecs
Kapichu
  • 3,346
  • 4
  • 19
  • 38

2 Answers2

7

The Rust optimiser makes the assumption that &mut T references are unique. For example, it might deduce that a particular piece of memory can be reused because a mutable reference to that memory exists but is never accessed again.

However, if you transmute a &T to a &mut T then you are able to create multiple mutable references to the same data. If the compiler makes this assumption, you could end up dereferencing a value that has been overwritten with something else.

This is just one example of how the compiler might make use of the assumption that mutable references are unique. In fact, the compiler is free to use this information in any way it sees fit — which could (and likely will) change from version to version.

Even if you think you have guaranteed that the reference isn't aliased, you can't always guarantee that users of your code won't create more references. Even if you think you can be sure of that, the existence of references is extremely subtle and it's very easy to miss one. For example when you call a method that takes &self, that's a reference.

Peter Hall
  • 53,120
  • 14
  • 139
  • 204
  • Thanks! But what if I make sure that I don't violate this assumption and only obtain a mutable reference when there are no other references to that place of memory? – Kapichu Nov 30 '19 at 18:35
  • @Kapichu That's the magic of UB, in the compiler's eyes, if it _could_ happen, it can totally take advantage of it. For example, another example of UB which is easier to explain: Owning a `T` where your value is uninitialized and __yet__ you still have a name for it is UB, even if you don't use it. [This](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=dc49e195300ee8fd0854737cf7948c82) even though we `forget` the value, is still UB. In other words, if the compiler sees you broke its contract it will eat your lunch and your kitchen sink. – Optimistic Peach Nov 30 '19 at 18:54
  • @Kapichu take a look at https://doc.rust-lang.org/reference/behavior-considered-undefined.html for some more information on that. – Optimistic Peach Nov 30 '19 at 18:58
6

The Rust compiler annotates &T function parameters with the LLVM noalias and readonly attributes (provided that T does not contain any UnsafeCell parts). The noalias attribute tells LLVM that the memory behind this pointer may only be written to through this pointer (and not through any other pointers), and the readonly attribute tells LLVM that it can't be written to through this pointer (but possibly other pointers). In combination, the two attributes allow the LLVM optimiser to assume the memory is not changed at all during the execution of this function, and the code can be optimised based on this assumption. The optimiser may reorder instructions or remove code in a way that is only safe to do if you actually stick to this contract.

Another way the conversion can lead to undefined behaviour is for statics: immutable statics without UnsafeCells will be placed into read-only memory, so if you actually write to them, your code will segfault.

For parameters with UnsafeCells the compiler does not emit the readonly attribute, and statics containing an UnsafeCell are placed into writable memory.

Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
  • 1
    _Currently_ Rust doesn't give the `noalias` hint [due to LLVM bugs](https://github.com/rust-lang/rust/issues/54878). – Peter Hall Dec 01 '19 at 18:35
  • Since I'm aware of this bug, but don't know its full scope, I tried [some example code on the playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=729fe7dbe29e32f243b0d0b785da6279) before posting this answer, and found that the current stable version of Rust does emit `noalias` for a reference to an integer (select the "LLVM IR" action to see it). I did not investigate any further, since it's not very relevant for this question in what cases Rust currently disables the attribute – it will eventually be re-enabled it anyway. – Sven Marnach Dec 01 '19 at 19:41