-
Notifications
You must be signed in to change notification settings - Fork 211
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
assume_safe
projection macro for convenient access to sub trees
#980
Comments
With a safety redesign being planned, the projection part might quickly become obsolete. A sub-tree fetching macro should always be useful, though. |
The main disadvantage of this API vs. annotations directly in the #[derive(NativeClass)]
#[gdnative::project]
struct MyScene {
child: Ref<Node>,
anim: Ref<AnimationPlayer>,
} equivalent to: #[gdnative::project]
struct Projected {
child: Ref<Node>,
anim: Ref<AnimationPlayer>,
}
#[derive(NativeClass)]
#[gdnative::project]
struct MyScene {
tree: Projected // not sure how this should look, what about the lifetime?
} |
Sure, but a new type like that also costs only around +3 lines in Rust. It can also improve readability by helping code organization when it comes to cases like the example I mentioned in OP: gdnative/examples/godot_tps_controller_port/src/player.rs Lines 31 to 59 in a2a85e1
...whose respective gdnative/examples/godot_tps_controller_port/src/player.rs Lines 64 to 88 in a2a85e1
The code organization would be a lot worse if we're forced to mix those fields together.
That would be the default case unless explicitly disallowed, no? There are however more challenges when it comes to mixing fields together. The example here looks pretty, but consider the case where the user also needs to export some property and keep some internal state here: #[derive(NativeClass)]
#[gdnative::project]
struct MyScene {
#[property]
#[bikeshed(skip)] // We can't just go by `Ref<_>` because these might be resources
child_template: Ref<PackedScene>,
#[property]`
#[bikeshed(skip)] // ...or references to other nodes set at run-time
other: Option<Ref<Node>>,
#[bikeshed(skip)]
internal_state: MySceneInner,
child: Ref<Node>,
anim: Ref<AnimationPlayer>,
} Suddenly it isn't as pretty anymore because we now have to add extra annotations on everything else.
The original type would stay untouched ( trait BikeshedProject: 'static {
type Target<'a>: 'a;
fn fetch(root: TRef<'a, Node>) -> Result<Self::Target<'a>, BikeshedError>;
fn claim(from: Self::Target<'a>) -> Self; // Using current terminology.
unsafe fn assume_safe(&self) -> Self::Target<'_>;
}
#[gdnative::project]
struct Projected {
child: Ref<Node>,
anim: Ref<AnimationPlayer>,
}
const _: () = {
use super::*;
pub struct SomeGeneratedNameToAvoidCollision<'a> {
pub child: <Ref<Node> as BikeshedProject>::Target<'a>, // TRef<'a, Node>
pub anim: <Ref<AnimationPlayer> as BikeshedProject>::Target<'a>, // TRef<'a, AnimationPlayer>
}
impl BikeshedProject for Projected {
type Target<'a> = SomeGeneratedNameToAvoidCollision<'a>;
// ...snip...
}
} The projected type could then be named from outside as |
This is of course all assuming that we still want A radical idea would be to represent Godot classes as traits instead, which can then be implemented generically for the reference types, but that will force |
While looking at #977 I realized that it can be cumbersome to access a fixed scene tree with many moving parts. The situation is similar with what happens with
Pin
in low level async code. There, the answer is projection as seen in thepin_project
crate.Similarly, we can implement a macro that allows a similar pattern, with projection being the default for convenience:
All the
TRef
s generated will have the same lifetime, but this should rarely be an issue for the intended use case of static sub-trees.Later, this can also be used as a building block for something like #758 (comment)
I believe this to be a better approach than putting
get_node
attributes directly in theNativeClass
for the flexibility it provides: the API in this proposal allows access to known sub trees with non-Rust roots, while the other one does not. This might not sound hugely important, but considering the use cases of:I think it's pretty obvious that separate types work better.
The text was updated successfully, but these errors were encountered: