blob: 6bea2ffb9cfd347ce6e4bbf43b4b74abf01f2213 [file] [log] [blame]
Brian Silvermanf3ec38b2022-07-06 20:43:36 -07001// Copyright 2022 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9// This example serves to demonstrate the experimental C++
10// reference wrappers. They exist because C++ references are not
11// the same as Rust references: C++ references may alias, whereas
12// Rust references may not.
13//
14// Standard autocxx behavior therefore introduces unsoundness when
15// C++ references are encountered and treated like Rust references.
16// (cxx has this soundness problem for Trivial types; autocxx
17// makes it worse in that the same problem applies even for
18// opaque types, because we make them sized such that we can allocate
19// them on the stack).
20//
21// Reference wrappers solve that problem because internally, they're
22// just pointers. On the other hand, they're awkward to use,
23// especially in the absence of the Rust "arbitrary self types"
24// feature.
25
26use autocxx::prelude::*;
27
28include_cpp! {
29 #include "input.h"
30 // This next line enables C++ reference wrappers
31 safety!(unsafe_references_wrapped)
32 generate!("Goat")
33 generate!("Field")
34}
35
36fn main() {
37 // Create a cxx::UniquePtr as normal for a Field object.
38 let field = ffi::Field::new().within_unique_ptr();
39 // We assume at this point that C++ has had no opportunity
40 // to retain any reference to the Field. That's not strictly
41 // true, due to RVO, but under all reasonable circumstances
42 // Rust currently has exclusive ownership of the Field we've
43 // been given.
44 // Therefore, at this point in the program, it's still
45 // OK to take Rust references to this Field.
46 let _field_rust_ref = field.as_ref();
47 // However, as soon as we want to pass a reference to the field
48 // back to C++, we have to ensure we have no Rust references
49 // in existence. So: we imprison the object in a "CppPin":
50 let field = ffi::cpp_pin_uniqueptr(field);
51 // We can no longer take Rust references to the field...
52 // let _field_rust_ref = field.as_ref();
53 // However, we can take C++ references. And use such references
54 // to call methods...
55 let another_goat = field.as_cpp_ref().get_goat();
56 // The 'get_goat' method in C++ returns a reference, so this is
57 // another CppRef, not a Rust reference.
58 assert_eq!(
59 another_goat
60 .describe() // returns a UniquePtr<CxxString>, there
61 // are no Rust or C++ references involved at this point.
62 .as_ref()
63 .unwrap()
64 .to_string_lossy(),
65 "This goat has 0 horns."
66 );
67}