Pointers, references, values

autocxx knows how to deal with C++ APIs which take C++ types:

  • By value
  • By reference (const or not)
  • By raw pointer
  • By std::unique_ptr
  • By std::shared_ptr
  • By std::weak_ptr
  • By rvalue reference (that is, as a move parameter)

(all of this is because the underlying cxx crate has such versatility). Some of these have some quirks in the way they're exposed in Rust, described below.

Passing between C++ and Rust by value

See the section on C++ types for the distinction between POD and non-POD types. POD types can be passed around however you like. Non-POD types can be passed into functions in various ways - see calling C++ functions for more details.

References and pointers

We follow cxx norms here. Specifically:

  • A C++ reference becomes a Rust reference
  • A C++ pointer becomes a Rust pointer.
  • If a reference is returned with an ambiguous lifetime, we don't generate code for the function
  • Pointers require use of unsafe, references don't necessarily.

That last point is key. If your C++ API takes pointers, you're going to have to use unsafe. Similarly, if your C++ API returns a pointer, you'll have to use unsafe to do anything useful with the pointer in Rust. This is intentional: a pointer from C++ might be subject to concurrent mutation, or it might have a lifetime that could disappear at any moment. As a human, you must promise that you understand the constraints around use of that pointer and that's what the unsafe keyword is for.

Exactly the same issues apply to C++ references in theory, but in practice, they usually don't. Therefore cxx has taken the view that we can "trust" a C++ reference to a higher degree than a pointer, and autocxx follows that lead (in fact we 'trust' references even slightly more than cxx). In practice, of course, references are rarely return values from C++ APIs so we rarely have to navel-gaze about the trustworthiness of a reference.

(See also the discussion of safety - if you haven't specified an unsafety policy, all C++ APIs require unsafe so the discussion is moot.

If you're given a C++ object by pointer, and you want to interact with it, you'll need to figure out the guarantees attached to the C++ object - most notably its lifetime. To see some of the decision making process involved see the Steam example.

cxx::UniquePtrs tips

We use cxx::UniquePtr in completely the normal way, but there are a few quirks which you're more likely to run into with autocxx.

  • You'll need to use .pin_mut() a lot - see the example at the bottom of C++ functions.
  • If you need to pass a raw pointer to a function, lots of unsafety is required - something like this:
       let mut a = ffi::A::make_unique();
       unsafe { ffi::TakePointerToA(std::pin::Pin::<&mut ffi::A>::into_inner_unchecked(a.pin_mut())) };
    This may be simplified in future.