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 Thing
s with &UnsafeCell<Thing>
tranformation I showcased in:
FYI, a good way to know if something is sound is to try and write the pointer code only using Rust references; and seeing *mut T as &UnsafeCell<T> (aliased upgradable-to-unique-on-demand pointer): impl<T : ?Sized> /* SomeExtensionTrait<T> for */ UnsafeCell<T> { /// Downgrade the uniqueness guarantee to only be applicable during /// the `'uniq` spans, instead of continually until the point of last usage. fn from_mut (it: &'_ mut T) -> &'_ UnsafeCell<T> { unsafe { ::coreā¦
- (basically, it allows to be a bit more explicit about where the "unchecked operations" mentioned by @2e71828 are happening).