Federico
Federico

Reputation: 2133

Why can unsafe code not move-out non-copy types from behind a raw pointer?

The Rust language disallows unsafe code from moving-out non-copy types from behind a raw pointer, reporting a compilation error for the following program:

use std::cell::UnsafeCell;

struct NonCopyType(u32);

fn main() {
    let unsafe_cell = UnsafeCell::new(NonCopyType(123));
    
    let ptr = unsafe_cell.get();

    // Disallowed, but the code will never access
    // the uninitialized unsafe cell after this.
    let _ = unsafe { *ptr };
}

Compilation error:

error[E0507]: cannot move out of `*ptr` which is behind a raw pointer
  --> src/main.rs:12:22
   |
12 |     let _ = unsafe { *ptr };
   |                      ^^^^ move occurs because `*ptr` has type `NonCopyType`, which does not implement the `Copy` trait

For more information about this error, try `rustc --explain E0507`.
error: could not compile `playground` due to previous error

What is the motivation of that error message? Is it because moving-out non-copy types from behind a raw pointer is error prone, even if the developer declared that he's expert enough to write unsafe code? Or is there some undefined behavior in the above program that I'm missing?

Upvotes: 6

Views: 2934

Answers (1)

eggyal
eggyal

Reputation: 125835

What is the motivation of that error message?

The safety invariants that a programmer must uphold in order to apply the dereference operator to a raw pointer are only that the pointer be dereferenceable. Where additional guarantees must be upheld (e.g. never using the referenced value again), Rust requires that some method with those requirements be used instead.

In this case, as loganfsmyth commented, there is std::ptr::read; or there's the inherent read method on the raw pointer types themselves. Thus:

use std::cell::UnsafeCell;

struct NonCopyType(u32);

fn main() {
    let unsafe_cell = UnsafeCell::new(NonCopyType(123));
    let ptr = unsafe_cell.get();
    let _ = unsafe { ptr.read() };
}

Is it because moving-out non-copy types from behind a raw pointer is error prone, even if the developer declared that he's expert enough to write unsafe code?

unsafe has nothing to do with expertise, and everything to do with taking responsibility for upholding certain invariants that the compiler would otherwise check for you; being explicit about the invariants each method requires to be upheld, and that each caller is upholding, is absolutely key to getting this right.

Rust could, I suppose, have overloaded the safety requirements of the dereference operator so that programmers need uphold different invariants depending on context. But that would be a horrible footgun, and make reasoning about the code extremely painful both for the person writing it and anyone who later reads it.

Upvotes: 2

Related Questions