Skip to content

Commit 7bea565

Browse files
committed
Remove impl AsObjectArg<T> for Gd<T> values
The idea is to require pass-by-ref for objects, in order to discourage accidental/unnecessary cloning. This is also consistent with AsArg by-refs, in particular `impl AsArg<Gd<T>>` when used in arrays and packed arrays. The downside is that it needs an extra `&` in some places, and one can no longer use pass-by-value to "consume" objects, ending their lifetime with the call. What is also unclear is whether there are situations where we can benefit from moving "into" the engine (perf-wise). So this may need some more discussion. One option is also to move toward using Copy for manually managed Gd<T> types, which would mean a) those could be passed by-value, and b) we would anyway lose the "consume" semantics. This is a more conservative choice for now, with minor syntactic drawbacks, but in line with AsArg<T> and the potential to expand in the future.
1 parent d570536 commit 7bea565

File tree

11 files changed

+34
-21
lines changed

11 files changed

+34
-21
lines changed

godot-core/src/obj/object_arg.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ use std::ptr;
2727
/// </div>
2828
2929
#[diagnostic::on_unimplemented(
30-
message = "The provided argument of type `{Self}` cannot be converted to a `Gd<{T}>` parameter"
30+
message = "Argument of type `{Self}` cannot be passed to an `impl AsObjectArg<{T}>` parameter",
31+
note = "If you pass by value, consider borrowing instead.",
32+
note = "See also `AsObjectArg` docs: https://godot-rust.github.io/docs/gdext/master/godot/meta/trait.AsObjectArg.html"
3133
)]
3234
pub trait AsObjectArg<T>
3335
where
@@ -41,6 +43,12 @@ where
4143
fn consume_arg(self) -> ObjectCow<T>;
4244
}
4345

46+
/*
47+
Currently not implemented for values, to be consistent with AsArg for by-ref builtins. The idea is that this can discover patterns like
48+
api.method(refc.clone()), and encourage better performance with api.method(&refc). However, we need to see if there's a notable ergonomic
49+
impact, and consider that for nodes, Gd<T> copies are relatively cheap (no ref-counting). There is also some value in prematurely ending
50+
the lifetime of a Gd<T> by moving out, so it's not accidentally used later.
51+
4452
impl<T, U> AsObjectArg<T> for Gd<U>
4553
where
4654
T: GodotClass + Bounds<Declarer = bounds::DeclEngine>,
@@ -54,6 +62,7 @@ where
5462
ObjectCow::Owned(self.upcast())
5563
}
5664
}
65+
*/
5766

5867
impl<T, U> AsObjectArg<T> for &Gd<U>
5968
where

godot-core/src/obj/script.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ impl<'a, T: ScriptInstance> SiMut<'a, T> {
386386
/// let name = this.base().get_name();
387387
/// godot_print!("name is {name}");
388388
/// // However, we cannot call methods that require `&mut Base`, such as:
389-
/// // this.base().add_child(node);
389+
/// // this.base().add_child(&node);
390390
/// Ok(Variant::nil())
391391
/// }
392392
/// # fn class_name(&self) -> GString { todo!() }

godot-core/src/obj/traits.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ pub trait WithBaseField: GodotClass + Bounds<Declarer = bounds::DeclUser> {
304304
/// fn process(&mut self, _delta: f64) {
305305
/// let node = Node::new_alloc();
306306
/// // fails because `add_child` requires a mutable reference.
307-
/// self.base().add_child(node);
307+
/// self.base().add_child(&node);
308308
/// }
309309
/// }
310310
///
@@ -344,7 +344,7 @@ pub trait WithBaseField: GodotClass + Bounds<Declarer = bounds::DeclUser> {
344344
/// impl INode for MyClass {
345345
/// fn process(&mut self, _delta: f64) {
346346
/// let node = Node::new_alloc();
347-
/// self.base_mut().add_child(node);
347+
/// self.base_mut().add_child(&node);
348348
/// }
349349
/// }
350350
///

itest/rust/src/engine_tests/native_audio_structures_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ fn native_audio_structure_out_parameter() {
113113
.cast::<SceneTree>();
114114

115115
tree.get_root().unwrap().add_child(&player);
116-
player.set_stream(generator);
116+
player.set_stream(&generator);
117117

118118
// Start playback so we can push audio frames through the audio pipeline.
119119
player.play();

itest/rust/src/engine_tests/node_test.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ fn node_get_node() {
2222

2323
let mut parent = Node3D::new_alloc();
2424
parent.set_name("parent");
25-
parent.add_child(child);
25+
parent.add_child(&child);
2626

2727
let mut grandparent = Node::new_alloc();
2828
grandparent.set_name("grandparent");
29-
grandparent.add_child(parent);
29+
grandparent.add_child(&parent);
3030

3131
// Directly on Gd<T>
3232
let found = grandparent.get_node_as::<Node3D>("parent/child");
@@ -74,7 +74,7 @@ fn node_scene_tree() {
7474
assert_eq!(err, global::Error::OK);
7575

7676
let mut tree = SceneTree::new_alloc();
77-
let err = tree.change_scene_to_packed(scene);
77+
let err = tree.change_scene_to_packed(&scene);
7878
assert_eq!(err, global::Error::OK);
7979

8080
// Note: parent + child are not owned by PackedScene, thus need to be freed

itest/rust/src/object_tests/object_arg_test.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use godot::obj::{Gd, NewAlloc, NewGd};
1313
use crate::framework::itest;
1414
use crate::object_tests::object_test::{user_refc_instance, RefcPayload};
1515

16+
/*
1617
#[itest]
1718
fn object_arg_owned() {
1819
with_objects(|manual, refc| {
@@ -22,6 +23,7 @@ fn object_arg_owned() {
2223
(a, b)
2324
});
2425
}
26+
*/
2527

2628
#[itest]
2729
fn object_arg_borrowed() {
@@ -43,6 +45,7 @@ fn object_arg_borrowed_mut() {
4345
});
4446
}
4547

48+
/*
4649
#[itest]
4750
fn object_arg_option_owned() {
4851
with_objects(|manual, refc| {
@@ -52,6 +55,7 @@ fn object_arg_option_owned() {
5255
(a, b)
5356
});
5457
}
58+
*/
5559

5660
#[itest]
5761
fn object_arg_option_borrowed() {
@@ -80,10 +84,10 @@ fn object_arg_option_none() {
8084

8185
// Will emit errors but should not crash.
8286
let db = ClassDb::singleton();
83-
let error = db.class_set_property(manual, "name", &Variant::from("hello"));
87+
let error = db.class_set_property(&manual, "name", &Variant::from("hello"));
8488
assert_eq!(error, global::Error::ERR_UNAVAILABLE);
8589

86-
let error = db.class_set_property(refc, "value", &Variant::from(-123));
90+
let error = db.class_set_property(&refc, "value", &Variant::from(-123));
8791
assert_eq!(error, global::Error::ERR_UNAVAILABLE);
8892
}
8993

@@ -106,14 +110,14 @@ fn object_arg_owned_default_params() {
106110
let b = ResourceFormatLoader::new_gd();
107111

108112
// Use direct and explicit _ex() call syntax.
109-
ResourceLoader::singleton().add_resource_format_loader(a.clone()); // by value
113+
ResourceLoader::singleton().add_resource_format_loader(&a);
110114
ResourceLoader::singleton()
111-
.add_resource_format_loader_ex(b.clone()) // by value
115+
.add_resource_format_loader_ex(&b)
112116
.done();
113117

114118
// Clean up (no leaks).
115-
ResourceLoader::singleton().remove_resource_format_loader(a);
116-
ResourceLoader::singleton().remove_resource_format_loader(b);
119+
ResourceLoader::singleton().remove_resource_format_loader(&a);
120+
ResourceLoader::singleton().remove_resource_format_loader(&b);
117121
}
118122

119123
// ----------------------------------------------------------------------------------------------------------------------------------------------

itest/rust/src/object_tests/object_swap_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ fn object_subtype_swap_argument_passing(ctx: &TestContext) {
119119

120120
let mut tree = ctx.scene_tree.clone();
121121
expect_panic("pass badly typed Gd<T> to Godot engine API", || {
122-
tree.add_child(node);
122+
tree.add_child(&node);
123123
});
124124

125125
swapped_free!(obj, node2);

itest/rust/src/object_tests/object_test.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ fn object_engine_freed_argument_passing(ctx: &TestContext) {
294294
// Destroy object and then pass it to a Godot engine API.
295295
node.free();
296296
expect_panic("pass freed Gd<T> to Godot engine API (T=Node)", || {
297-
tree.add_child(node2);
297+
tree.add_child(&node2);
298298
});
299299
}
300300

@@ -325,7 +325,7 @@ fn object_user_freed_argument_passing() {
325325
// Destroy object and then pass it to a Godot engine API (upcast itself works, see other tests).
326326
obj.free();
327327
expect_panic("pass freed Gd<T> to Godot engine API (T=user)", || {
328-
engine.register_singleton("NeverRegistered", obj2);
328+
engine.register_singleton("NeverRegistered", &obj2);
329329
});
330330
}
331331

@@ -848,7 +848,7 @@ fn object_get_scene_tree(ctx: &TestContext) {
848848
let node = Node3D::new_alloc();
849849

850850
let mut tree = ctx.scene_tree.clone();
851-
tree.add_child(node);
851+
tree.add_child(&node);
852852

853853
let count = tree.get_child_count();
854854
assert_eq!(count, 1);

itest/rust/src/object_tests/onready_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ fn init_attribute_node_key_lifecycle() {
157157

158158
let mut child = Node::new_alloc();
159159
child.set_name("child");
160-
obj.add_child(child);
160+
obj.add_child(&child);
161161

162162
obj.notify(NodeNotification::READY);
163163

itest/rust/src/object_tests/virtual_methods_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,7 @@ fn test_format_loader(_test_context: &TestContext) {
519519
.unwrap();
520520
assert!(resource.try_cast::<BoxMesh>().is_ok());
521521

522-
loader.remove_resource_format_loader(format_loader);
522+
loader.remove_resource_format_loader(&format_loader);
523523
}
524524

525525
#[itest]

0 commit comments

Comments
 (0)