-
Notifications
You must be signed in to change notification settings - Fork 156
/
Copy pathmain.rs
118 lines (105 loc) · 4.17 KB
/
main.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// 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.
// Necessary to be able to call methods on reference wrappers.
// For that reason, this example only builds on nightly Rust.
#![feature(arbitrary_self_types)]
use autocxx::prelude::*;
use std::pin::Pin;
include_cpp! {
#include "input.h"
// This next line enables C++ reference wrappers
// This is what requires the 'arbitrary_self_types' feature.
safety!(unsafe_references_wrapped)
generate!("Goat")
generate!("Field")
}
impl ffi::Goat {
// Methods can be called on a CppRef<T> or &CppRef<T>
fn bleat(self: CppRef<Self>) {
println!("Bleat");
}
}
trait FarmProduce {
// Traits can be defined on a CppRef<T> so long as Self: Sized
fn sell(self: &CppRef<Self>)
where
Self: Sized;
}
impl FarmProduce for ffi::Goat {
fn sell(self: &CppRef<Self>) {
println!("Selling goat");
}
}
trait FarmArea {
fn maintain(self: CppRef<Self>);
}
impl FarmArea for ffi::Field {
fn maintain(self: CppRef<Self>) {
println!("Maintaining");
}
}
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 = CppUniquePtrPin::new(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 in C++. Quite often those methods will
// return other references, like this.
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.
// We can still use these C++ references to call Rust methods,
// so long as those methods have a "self" type of a
// C++ reference not a Rust reference.
another_goat.clone().bleat();
another_goat.sell();
// But most commonly, C++ references are simply used as the 'this'
// type when calling other C++ methods, like this.
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."
);
// We can even use trait objects, though it's a bit of a fiddle.
let farm_area: Pin<Box<dyn FarmArea>> = ffi::Field::new().within_box();
let farm_area = CppPin::from_pinned_box(farm_area);
farm_area.as_cpp_ref().maintain();
}