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_pointers)]
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. CppRef
s 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>
impl<'a, T: ?Sized> CppRef<'a, T>
Sourcepub unsafe fn as_ref(&self) -> &T
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.
Sourcepub fn const_cast(&self) -> CppMutRef<'a, T>
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.
Sourcepub fn lifetime_cast(&self) -> PhantomReferent<T>
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 CppRef
s using
AsCppRef::as_cpp_ref
.
§Safety
Because CppRef
s 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.