Skip to content

Commit b9b81ed

Browse files
committed
Discussion about impl AsObjectArg<T> for &Option<Gd<T>> (outer ref)
It's relatively common that Godot APIs return `Option<Gd<T>>` or pass this type in virtual functions. To avoid excessive `as_ref()` calls, we **could** directly support `&Option<Gd>` in addition to `Option<&Gd>`. However, this is currently not done as it hides nullability, especially in situations where a return type is directly propagated: api(create_obj().as_ref()) api(&create_obj()) While the first is slightly longer, it looks different from a function create_obj() that returns Gd<T> and thus can never be null. It's also quite idiomatic to use as_ref() for inner-option transforms in Rust. For now, the code is still added but inactive.
1 parent af53e74 commit b9b81ed

File tree

3 files changed

+48
-4
lines changed

3 files changed

+48
-4
lines changed

godot-core/src/obj/object_arg.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,37 @@ where
9797
}
9898
}
9999

100+
/*
101+
It's relatively common that Godot APIs return `Option<Gd<T>>` or pass this type in virtual functions. To avoid excessive `as_ref()` calls, we
102+
**could** directly support `&Option<Gd>` in addition to `Option<&Gd>`. However, this is currently not done as it hides nullability,
103+
especially in situations where a return type is directly propagated:
104+
api(create_obj().as_ref())
105+
api(&create_obj())
106+
While the first is slightly longer, it looks different from a function create_obj() that returns Gd<T> and thus can never be null.
107+
In some scenarios, it's better to immediately ensure non-null (e.g. through `unwrap()`) instead of propagating nulls to the engine.
108+
It's also quite idiomatic to use as_ref() for inner-option transforms in Rust.
109+
110+
impl<T, U> AsObjectArg<T> for &Option<U>
111+
where
112+
T: GodotClass + Bounds<Declarer = bounds::DeclEngine>,
113+
for<'a> &'a U: AsObjectArg<T>,
114+
{
115+
fn as_object_arg(&self) -> ObjectArg<T> {
116+
match self {
117+
Some(obj) => obj.as_object_arg(),
118+
None => ObjectArg::null(),
119+
}
120+
}
121+
122+
fn consume_arg(self) -> ObjectCow<T> {
123+
match self {
124+
Some(obj) => obj.consume_arg(),
125+
None => Gd::null_arg().consume_arg(),
126+
}
127+
}
128+
}
129+
*/
130+
100131
impl<T> AsObjectArg<T> for ObjectNullArg<T>
101132
where
102133
T: GodotClass + Bounds<Declarer = bounds::DeclEngine>,

itest/rust/src/object_tests/object_arg_test.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,18 @@ fn object_arg_option_borrowed() {
7171
});
7272
}
7373

74+
/*
75+
#[itest]
76+
fn object_arg_option_borrowed_outer() {
77+
with_objects(|manual, refc| {
78+
let db = ClassDb::singleton();
79+
let a = db.class_set_property(&Some(manual), "name", &Variant::from("hello"));
80+
let b = db.class_set_property(&Some(refc), "value", &Variant::from(-123));
81+
(a, b)
82+
});
83+
}
84+
*/
85+
7486
#[itest]
7587
fn object_arg_option_borrowed_mut() {
7688
// If you have an Option<&mut Gd<T>>, you can use as_deref() to get Option<&Gd<T>>.
@@ -94,10 +106,10 @@ fn object_arg_option_none() {
94106

95107
// Will emit errors but should not crash.
96108
let db = ClassDb::singleton();
97-
let error = db.class_set_property(&manual, "name", &Variant::from("hello"));
109+
let error = db.class_set_property(manual.as_ref(), "name", &Variant::from("hello"));
98110
assert_eq!(error, global::Error::ERR_UNAVAILABLE);
99111

100-
let error = db.class_set_property(&refc, "value", &Variant::from(-123));
112+
let error = db.class_set_property(refc.as_ref(), "value", &Variant::from(-123));
101113
assert_eq!(error, global::Error::ERR_UNAVAILABLE);
102114
}
103115

itest/rust/src/register_tests/rpc_test.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,13 @@ impl RpcTest {
7777
fn node_enters_tree() {
7878
let node = RpcTest::new_alloc();
7979

80-
// Registering is done in `UserClass::__before_ready()`, and it requires a multiplayer api to exist.
80+
// Registering is done in `UserClass::__before_ready()`, and it requires a multiplayer API to exist.
8181
let mut scene_tree = Engine::singleton()
8282
.get_main_loop()
8383
.unwrap()
8484
.cast::<SceneTree>();
85-
scene_tree.set_multiplayer(&MultiplayerApi::create_default_interface());
85+
scene_tree.set_multiplayer(MultiplayerApi::create_default_interface().as_ref());
86+
8687
let mut root = scene_tree.get_root().unwrap();
8788
root.add_child(&node);
8889
root.remove_child(&node);

0 commit comments

Comments
 (0)