blob: 6bea2ffb9cfd347ce6e4bbf43b4b74abf01f2213 [file] [log] [blame]
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// This example serves to demonstrate the experimental C++
// reference wrappers. They exist because C++ references are not
// the same as Rust references: C++ references may alias, whereas
// Rust references may not.
//
// Standard autocxx behavior therefore introduces unsoundness when
// C++ references are encountered and treated like Rust references.
// (cxx has this soundness problem for Trivial types; autocxx
// makes it worse in that the same problem applies even for
// opaque types, because we make them sized such that we can allocate
// them on the stack).
//
// Reference wrappers solve that problem because internally, they're
// just pointers. On the other hand, they're awkward to use,
// especially in the absence of the Rust "arbitrary self types"
// feature.
use autocxx::prelude::*;
include_cpp! {
#include "input.h"
// This next line enables C++ reference wrappers
safety!(unsafe_references_wrapped)
generate!("Goat")
generate!("Field")
}
fn main() {
// Create a cxx::UniquePtr as normal for a Field object.
let field = ffi::Field::new().within_unique_ptr();
// We assume at this point that C++ has had no opportunity
// to retain any reference to the Field. That's not strictly
// true, due to RVO, but under all reasonable circumstances
// Rust currently has exclusive ownership of the Field we've
// been given.
// Therefore, at this point in the program, it's still
// OK to take Rust references to this Field.
let _field_rust_ref = field.as_ref();
// However, as soon as we want to pass a reference to the field
// back to C++, we have to ensure we have no Rust references
// in existence. So: we imprison the object in a "CppPin":
let field = ffi::cpp_pin_uniqueptr(field);
// We can no longer take Rust references to the field...
// let _field_rust_ref = field.as_ref();
// However, we can take C++ references. And use such references
// to call methods...
let another_goat = field.as_cpp_ref().get_goat();
// The 'get_goat' method in C++ returns a reference, so this is
// another CppRef, not a Rust reference.
assert_eq!(
another_goat
.describe() // returns a UniquePtr<CxxString>, there
// are no Rust or C++ references involved at this point.
.as_ref()
.unwrap()
.to_string_lossy(),
"This goat has 0 horns."
);
}