Single mutable reference rule and FFI

Not necessarily, it depends on what is being done.

That being said, I agree that is is quite dangerous to use Rust's &mut with long-lived pointers in C, since their being long-lived will most probably lead to aliasing, and when that happens we are reaching the & state, which can only be upgraded back to allowing mutation if the mutated fields were UnsafeCell-wrapped, and if the Rust side had never re-created an &mut reference.

That is, we have the following scenarios:

  • //! Scenario "Rust creates a `&mut` in between"
    let r1 = &mut thing;
    give_to_ffi(r1 as *mut _);
    let r2 = &mut thing;
    ffi_mutate();
    

    which is always UB;

  • //! Scenario "Rust creates a `&` in between"
    
    let r1 = &mut thing;
    give_to_ffi(r1 as *mut _);
    let r2 = &thing;
    ffi_mutate(); // Through direct C mutation or by calling back to Rust
    

    which is UB if the mutated fields are not wrapped within an UnsafeCell or if the called-back-Rust-code is fed an &mut thing;

  • //! Scenario 'Rust does "nothing" in between'
    let r1 = &mut thing;
    give_to_ffi(r1 as *mut _);
    stuff(); /* but nothing _w.r.t._ `r1` or anything transitively pointed to by it */
    ffi_mutate(); // FFI
    

    which is fine, even if the called-back-Rust code is fed an &mut thing.

This can be showcased if replacing the *mut Things with &UnsafeCell<Thing> tranformation I showcased in:

  • (basically, it allows to be a bit more explicit about where the "unchecked operations" mentioned by @2e71828 are happening).
1 Like