Brian Silverman | f3ec38b | 2022-07-06 20:43:36 -0700 | [diff] [blame^] | 1 | // 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 | |
| 26 | use autocxx::prelude::*; |
| 27 | |
| 28 | include_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 | |
| 36 | fn 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 | } |