-
Notifications
You must be signed in to change notification settings - Fork 366
Expose a nonconst reference wrapper binding #537
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Is using |
Yes, that is effectively equivalent to the semantics of |
Is there a reason to have this as a type rather than an attribute? I'm working with a library that uses a lot of cell-semantics nonconst referenes, and exposing them to Rust as unsafe fn SetActorLocationAndRotation(
#[nonconst] self: &AActor,
NewLocation: FVector,
// ...
) -> bool; to drop the const-qualification. Anyone using these types is already well aware that they have Cell-semantic shared mutability, and adding any more ceremony to the reference would be needlessly verbose. (EDIT: well, while |
Would using |
I'll keep it in mind. I'm mostly concerned about people accidentally stumbling into cases that use nonconst methods, potentially even in a non threadsafe manner (thus correctly nonconst per cxx's modern dialect of C++ it interfaces to), but which aren't compatible with Rust's exclusive ownership concept. Especially with additional tooling such as autocxx, it's not that difficult to just use a It's also not entirely decided what exactly exclusive ownership means w.r.t. FFI, as I understand it, especially when you have multiple layers of FFI boundaries passing In these cases, it's much simpler to not assert language-level uniqueness on the Rust side. It's fine for cxx to define the C++ dialect it works with as not including APIs with method receivers which can't be directly translated as And at this point, I'm thinking, maybe it just should. There's no shortcut to triple checking your FFI invariants line up. cxx can automate a common subset of it, but you're still on the hook to make sure you match your FFI after adopting cxx's as well. |
I've been playing around with this here (implementation here). The ergonomics seem OK - no worse than More things to think about:
|
Would building
I don't think the original |
Even worse than just adding writes, it adds atomic writes. And unlike |
I also had the case where I wanted to make functionality available that was implemented with non-const C++ member functions, but which only performed read-actions. So, the natural representations in the Rust API I created needed to have I created this Rust equivalent to C++'s pub(crate) unsafe fn cxx_const_cast<T: UniquePtrTarget>(value: &T) -> Pin<&mut T> {
#![inline]
//! Presents an immutable reference as a mutable one for the purpose of calling a CXX bridge
//! function (casts the constness away). The mutable reference must not actually be mutated!
//! (Otherwise, bring mutability into the Rust code.)
//!
//! This is meant as a last resort to avoid having to write a C++ wrapper function every
//! time some API function isn't declared as `const` on the C++ side, even though it should
//! be. In that wrapper, the same thing would be done with a C++ `const_cast<...>(...)`
//! anyway.
#[allow(clippy::cast_ref_to_mut)]
Pin::new_unchecked(&mut *(value as *const T as *mut T))
} It worked well. Later, I discovered this warning, however, which I don't fully understand, but which seems to be very relevant:
https://stackoverflow.com/a/54242058/10749231 Perhaps, this could be solved by preventing such compiler optimizations? I now have no need for the function anymore. I had a point where more and more lifetimes came into play with the |
This has very interesting implications for the SharedPtr and UniquePtr API. In Rust, it's not sound to expose a safe deref from
&SharedPtr<T>
to&mut T
/Pin<&mut T>
because the lifetime system is not expressive enough to rule out calling it twice, and calling it twice produces overlapping &mut references which is UB in Rust. However, in C++ it's not, so it's safe to expose&'a SharedPtr<T>
toNonConstRef<'a, T>
in Rust and haveNonConstRef<T>
become mutableT&
on the C++ side, as long as there is no conversionNonConstRef<'a, T>
to&'a mut T
(&'a T
is fine). This is a safe way to capture the semantics ofT& std::shared_ptr<T>::operator*() const noexcept
.Needs design, but possibly we can tie this to https://en.cppreference.com/w/cpp/utility/functional/reference_wrapper and piggy back on
ReferenceWrapper<T>
.The text was updated successfully, but these errors were encountered: