Struct autocxx::CppRef

source ·
#[repr(transparent)]
pub struct CppRef<'a, T: ?Sized> { /* private fields */ }
Expand description

A C++ const reference. These are different from Rust’s &T in that these may exist even while the object is mutated elsewhere. See also CppMutRef for the mutable equivalent.

The key rule is: we never dereference these in Rust. Therefore, any UB here cannot manifest within Rust, but only across in C++, and therefore they are equivalently safe to using C++ references in pure-C++ codebases.

Important: you might be wondering why you’ve never encountered this type. These exist in autocxx-generated bindings only if the unsafe_references_wrapped safety policy is given. This may become the default in future.

Usage

These types of references are pretty useless in Rust. You can’t do field access. But, you can pass them back into C++! And specifically, you can call methods on them (i.e. use this type as a this). So the common case here is when C++ gives you a reference to some type, then you want to call methods on that reference.

Calling methods

As noted, one of the main reasons for this type is to call methods. Unfortunately, that depends on unstable Rust features. If you can’t call methods on one of these references, check you’re using nightly and add #![feature(arbitrary_self_types)] to your crate.

Lifetimes and cloneability

Although these references implement C++ aliasing semantics, they do attempt to give you Rust lifetime tracking. This means if a C++ object gives you a reference, you won’t be able to use that reference after the C++ object is no longer around.

This is usually what you need, since a C++ object will typically give you a reference to part of itself or something that it owns. But, if you know that the returned reference lasts longer than its vendor, you can use lifetime_cast to get a long-lived version.

On the other hand, these references do not give you Rust’s exclusivity guarantees. These references can be freely cloned, and using CppRef::const_cast you can even make a mutable reference from an immutable reference.

Field access

Field access would be achieved by adding C++ get and/or set methods. It’s possible that a future version of autocxx could generate such getters and setters automatically, but they would need to be unsafe because there is no guarantee that the referent of a CppRef is actually what it’s supposed to be, or alive. CppRefs may flow from C++ to Rust via arbitrary means, and with sufficient uses of get and set it would even be possible to create a use-after-free in pure Rust code (for instance, store a CppPin in a struct field, get a CppRef to its referent, then use a setter to reset that field of the struct.)

Deref

This type implements Deref because that’s the mechanism that the unstable Rust arbitrary_self_types features uses to determine callable methods. However, actually calling Deref::deref is not permitted and will result in a compilation failure. If you wish to create a Rust reference from the C++ reference, see CppRef::as_ref.

Nullness

Creation of a null C++ reference is undefined behavior (because such a reference can only be created by dereferencing a null pointer.) However, in practice, they exist, and we need to be compatible with pre-existing C++ APIs even if they do naughty things like this. Therefore this CppRef type does allow null values. This is a bit unfortunate because it means Option<CppRef<T>> occupies more space than CppRef<T>.

Dynamic dispatch

You might wonder if you can do this:

let CppRef<dyn Trait> = ...; // obtain some CppRef<concrete type>

Dynamic dispatch works so long as you’re using nightly (we require another unstable feature, dispatch_from_dyn). But we need somewhere to store the trait object, and CppRef isn’t it – a CppRef can only store a simple pointer to something else. So, you need to store the trait object in a Box or similar:

trait SomeTrait {
   fn some_method(self: CppRef<Self>)
}
impl SomeTrait for ffi::Concrete {
  fn some_method(self: CppRef<Self>) {}
}
let obj: Pin<Box<dyn SomeTrait>> = ffi::Concrete::new().within_box();
let obj = CppPin::from_pinned_box(obj);
farm_area.as_cpp_ref().some_method();

Implementation notes

Internally, this is represented as a raw pointer in Rust. See the note above about Nullness for why we don’t use core::ptr::NonNull.

Implementations§

source§

impl<'a, T: ?Sized> CppRef<'a, T>

source

pub fn as_ptr(&self) -> *const T

Retrieve the underlying C++ pointer.

source

pub unsafe fn as_ref(&self) -> &T

Get a regular Rust reference out of this C++ reference.

Safety

Callers must guarantee that the referent is not modified by any other C++ or Rust code while the returned reference exists. Callers must also guarantee that no mutable Rust reference is created to the referent while the returned reference exists.

Callers must also be sure that the C++ reference is properly aligned, not null, pointing to valid data, etc.

source

pub fn from_ptr(ptr: *const T) -> Self

Create a C++ reference from a raw pointer.

source

pub fn const_cast(&self) -> CppMutRef<'a, T>

Create a mutable version of this reference, roughly equivalent to C++ const_cast.

The opposite is to use AsCppRef::as_cpp_ref on a CppMutRef to obtain a CppRef.

Safety

Because we never dereference a CppRef in Rust, this cannot create undefined behavior within Rust and is therefore not unsafe. It is however generally unwise, just as it is in C++. Use sparingly.

source

pub fn lifetime_cast(&self) -> PhantomReferent<T>

Extend the lifetime of the returned reference beyond normal Rust borrow checker rules.

Normally, a reference can’t be used beyond the lifetime of the object which gave it to you, but sometimes C++ APIs can return references to global or other longer-lived objects. In such a case you should use this method to get a longer-lived reference.

Usage

When you’re given a C++ reference and you know its referent is valid for a long time, use this method. Store the resulting PhantomReferent somewhere in Rust with an equivalent lifetime. That object can then vend longer-lived CppRefs using AsCppRef::as_cpp_ref.

Safety

Because CppRefs are never dereferenced in Rust, misuse of this API cannot lead to undefined behavior in Rust and is therefore not unsafe. Nevertheless this can lead to UB in C++, so use carefully.

Trait Implementations§

source§

impl<'a, T: ?Sized> Clone for CppRef<'a, T>

source§

fn clone(&self) -> Self

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<'a, T: ?Sized> Deref for CppRef<'a, T>

§

type Target = *const T

The resulting type after dereferencing.
source§

fn deref(&self) -> &Self::Target

Dereferences the value.
source§

impl<'a, T> From<CppMutRef<'a, T>> for CppRef<'a, T>

source§

fn from(mutable: CppMutRef<'a, T>) -> Self

Converts to this type from the input type.
source§

impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<CppRef<'_, U>> for CppRef<'_, T>

Auto Trait Implementations§

§

impl<'a, T: ?Sized> RefUnwindSafe for CppRef<'a, T>where T: RefUnwindSafe,

§

impl<'a, T> !Send for CppRef<'a, T>

§

impl<'a, T> !Sync for CppRef<'a, T>

§

impl<'a, T: ?Sized> Unpin for CppRef<'a, T>

§

impl<'a, T: ?Sized> UnwindSafe for CppRef<'a, T>where T: RefUnwindSafe,

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for Twhere U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> ToOwned for Twhere T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.