-
-
Notifications
You must be signed in to change notification settings - Fork 212
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
Derive GodotClass
for traits
#426
Comments
Both 334 and this are binding shortfalls of godot-rust that other languages currently don't suffer from. Inherited Resources is how I implement the gang of four command pattern. It allows me to store context of the scene for things like achievements, game events, cheats, and context-sensitive data. This feature would keep my workflow unharmed! Here's a video of what the proposed feature would need to achieve: output.webm
Enums might be a more rusty way to do it, though this was where I went first with my inheritance riddled workflow. awestlake87 provided a close work-around here, but it doesn't support restricting the type, making it easy to put invalid data in. |
Interesting idea, but there are some hurdles we need to address:
Maybe other languages that have inheritance. Pretty sure Go bindings face the same issue. Before looking at how we can implement this, we should decide what use case we actually want to solve. Because if we can't use traits to emulate full-blown abstract base classes due to lack of state, we need to see what remains. One thing mentioned is:
That is, GDScript/editor type safety. Others? |
Yeah you're right, honestly it seems like this is a failure on godot's part for not supporting traits or interfaces. C# also suffers from a similar problem, but is able to use inheritance to work around it. It doesn't seem like godot-rust's responsibility to make a fake inheritance just to play nice with the editor. The most elegant solution seems to be to add traits to Godot itself, then Go, C#, and rust will bind better.
Specifically editor type safety, as in, the UI that allows me to drag and drop assets visually within the editor. Using inheritance means that the editor will smartly restrict what I can create or drag and drop into exported resource and node parameters. I don't care for gdscript. Just want to work nicely with the editor. |
I was thinking that it might not make much sense to use just traits for this. Rather, I think a struct/trait pair, much like the current ClassName / ClassNameVirtual system, would be the way to go. That would be something similar to how Edit: this setup would also allow you to upcast types to the traits' "companion structs" (like in GLib) without resorting to |
I made a small POC of having inheritance between Rust classes: PgBiel@0f4131c and PgBiel@936dd1d It worked and I could instantiate So this seems to indicate that it's possible to make some form of "abstract/virtual class" within Rust. We'll likely need some trait work (using associated types) to indicate the companion/virtual trait for a particular class. We could also consider asking the abstract class' children to implement its parent's |
Thanks, that looks very interesting! Adding Rust inheritance will add quite a few things that we need to thoroughly consider:
Probably a lot of edge cases I'm currently not thinking of 🙂 |
Those are definitely important concerns; thanks for pointing them out. Regarding In particular, we could try to do something like impl<T> Inherits<T> for Child
where Parent: Inherits<T> {} It did seem to compile, but I haven't tested any possible interaction with that yet (I could upcast the |
Wouldn't it be possible to handle Rust traits for GodotClass structs in other way? I mean something like: // trait declaration
#[godot_api_trait]
pub trait MyGodotTrait {
#[func]
fn my_trait_function_to_implement(&self) -> i32
#[func]
fn my_trait_function_with_default(&self) -> i32 {
42
}
}
// trait implementation
#[derive(GodotClass)]
#[class(base=Object, init)]
pub struct MyGodotClass;
#[godot_api_trait]
impl MyGodotTrait for MyGodotClass {
#[func]
fn my_trait_function_to_implement(&self) -> i32 {
15
}
} This way we avoid the problem of Rust inheritance - we can use the regular Rust trait logic, just register the methods accordingly to struct, most notably its Godot signature. From Godot point of view the
The obvious red flag is of course more macro magic, unfortunately in current form, it seems inevitable. Another is the requirement for the trait declaration to be compiled before the trait implementation - I'm not exactly sure how the Rust compiler works in that regard, but in the worst-case scenario it could be possible to move the traits to a second library, which will be added as a dependency to the one making use of traits - the same it is done for procedural macros in regular Rust currently. I would prefer this to go more in this way rather than trying to create an inheritance in Rust and between Rust-defined classes. Firstly - it is more Rusty this way, and Rust users coming in Lastly - Godot can provide a trait system themselves in the future, as there are proposals for this already and they are discussed. Using Rust trait system in the way above shouldn't clash with Godot's traits in contrary with EDIT: Additional hurdle is that |
What if exporting trait impls were done really naively, like just export the methods in the trait impl the same as for those in the struct impl. But then also export a method |
Previously, in Godot 3 bindings, it was possible to something similar to what @StatisMike described by hand: it was possible to create custom pub trait HasProperty {
fn prop(&self) -> &GodotString;
fn prop_mut(&mut self) -> &mut GodotString;
}
pub fn register_prop<N>(builder: &ClassBuilder<N>)
where N: HasProperty + NativeClass, N::UserData: UserDataMapMut
{
builder.property("prop")
.with_getter(|s, _| s.prop())
.with_setter(|s, _, v| s.prop_mut() = v)
.with_default("").done();
// Similarly it's also possible to register methods, signals etc.
// Sturcts which impl this trait will need to run this `register` method inside their own `register`s.
} AFAIK there's currently no public API for manual registration of properties/methods/signals etc., so it's impossible to implement this pattern. With builder API (#4) it would be possible to do that again, but I also think this manual registration method can be provided by proc macro placed on top of |
similar to #334, a possible way to add restricted inheritance would be to allow traits to derive
GodotClass
. This would turn that trait into an abstract class. Then later we could allow people to inherit from this base class by implementing the trait.i haven't thought out many details of this, but it has the advantage over doing it for enums that we dont need to enumerate all possible children immediately. it's also more similar in both godot and rust.
we could possibly integrate this with traits in gdscript when they're added: godotengine/godot-proposals#6416
We'd probably need to make the trait a subtrait of
Inherits<BaseClass>
.The text was updated successfully, but these errors were encountered: