Document DynGd<_, D> type inference.#1142
Conversation
|
API docs are being generated and will be shortly available at: https://godot-rust.github.io/docs/gdext/pr-1142 |
Bromeon
left a comment
There was a problem hiding this comment.
Thanks!
Apart from the : 'static bound users need to add, are there any other ergonomic downsides of this change?
: 'static is arguably a bit annoying since that is very unusual for most traits, but I guess if it makes inference clearer, that's probably worth it 🤔
godot-core/src/obj/dyn_gd.rs
Outdated
| /// // Type can't be inferred. | ||
| /// // Would result in confusing compilation error | ||
| /// // since compiler would try to enforce 'static **lifetime** on our reference. | ||
| /// // let mut dyn_gd = Monster::new_gd().into_dyn(); | ||
| /// // no_inference(&mut *dyn_gd.dyn_bind_mut()); |
There was a problem hiding this comment.
Also, instead of **lifetime**, you could write *lifetime* (&'static mut ...).
| /// fn deal_damage(h: &mut dyn Health) { /* ... */ } | ||
| /// | ||
| /// fn no_inference(i: &mut dyn NoInference) { /* ... */ } |
There was a problem hiding this comment.
| /// fn deal_damage(h: &mut dyn Health) { /* ... */ } | |
| /// | |
| /// fn no_inference(i: &mut dyn NoInference) { /* ... */ } | |
| /// // Two example functions accepting trait object, to check type inference. | |
| /// fn deal_damage(h: &mut dyn Health) { /* ... */ } | |
| /// fn no_inference(i: &mut dyn NoInference) { /* ... */ } |
godot-core/src/obj/dyn_gd.rs
Outdated
| /// If class implements more than one `#[godot_dyn]` trait, type inference only works when the trait we dereference to explicitly declares | ||
| /// `'static` as its supertrait. |
There was a problem hiding this comment.
| /// If class implements more than one `#[godot_dyn]` trait, type inference only works when the trait we dereference to explicitly declares | |
| /// `'static` as its supertrait. | |
| /// If a class implements more than one `AsDyn` relation (usually via `#[godot_dyn]`), type inference will only work when the trait | |
| /// used for `D` explicitly declares a `: 'static` bound. |
godot-core/src/obj/dyn_gd.rs
Outdated
| /// | ||
| /// If class implements more than one `#[godot_dyn]` trait, type inference only works when the trait we dereference to explicitly declares | ||
| /// `'static` as its supertrait. | ||
| /// Otherwise, if only one `#[godot_dyn]` trait is implemented for a given class, the type can be properly inferred. |
There was a problem hiding this comment.
| /// Otherwise, if only one `#[godot_dyn]` trait is implemented for a given class, the type can be properly inferred. | |
| /// Otherwise, if only one `impl AsDyn` is present for a given class, the type can always be inferred. |
86bd36a to
a098d0a
Compare
Personally I haven't found any downsides 🤔. I'll try to make another deep dive in a month or two to figure out what is going on – but for now, especially for I also updated comment related to #1145 (I was too focused to make it work ASAP to take care of such details 😅). |
a098d0a to
901e79a
Compare
| // Type can be inferred because `Health` explicitly declares | ||
| // 'static as its supertrait. | ||
| let mut obj = original_obj.clone().into_dyn(); |
There was a problem hiding this comment.
| // Type can be inferred because `Health` explicitly declares | |
| // 'static as its supertrait. | |
| let mut obj = original_obj.clone().into_dyn(); | |
| // Type can be inferred because `Health` explicitly declares a 'static bound. | |
| let mut obj = original_obj.clone().into_dyn(); |
Line width 140-150 (see code style)
Also, 'static isn't a trait, so it's not a supertrait either.
| /// # Type inference | ||
| /// | ||
| /// If a class implements more than one `AsDyn<D>` relation (usually via `#[godot_dyn]`), type inference will only work when the trait | ||
| /// used for `D` explicitly declares a `: 'static` bound. |
There was a problem hiding this comment.
This section could probably come last, no?
Exporting is definitely more important (arguably also more than dyn-re-enrichment).
There was a problem hiding this comment.
hmm, I think you are right – albeit I'm finding Polymorphic dyn re-enrichment a bit more important 🤔 (reasoning – the first two sections explain how to use it and how it works, while following ones focus on documenting various quirks)
I put Type inference as the last section
godot-core/src/obj/dyn_gd.rs
Outdated
| /// fn deal_damage(h: &mut dyn Health) { /* ... */ } | ||
| /// fn no_inference(i: &mut dyn NoInference) { /* ... */ } | ||
| /// | ||
| /// // Type can be inferred since `static supertrait is explicitly declared for Health trait. |
There was a problem hiding this comment.
| /// // Type can be inferred since `static supertrait is explicitly declared for Health trait. | |
| /// // Type can be inferred since 'static bound is explicitly declared for Health trait. |
No backtick, but apostrophe -- and no supertrait
| // Example symbols | ||
|
|
||
| trait Health { | ||
| // 'static supertrait must be explicitly declared to make type inference work. |
There was a problem hiding this comment.
| // 'static supertrait must be explicitly declared to make type inference work. | |
| // 'static bound must be explicitly declared to make type inference work. |
|
|
||
| // Not recommended – for presentational purposes only. | ||
| // Works because 'static bound on type is enforced in function signature. | ||
| // i.e. This wouldn't work with fn get_instance_id(...). |
There was a problem hiding this comment.
| // i.e. This wouldn't work with fn get_instance_id(...). | |
| // I.e. this wouldn't work with fn get_instance_id(...). |
godot-core/src/registry/callbacks.rs
Outdated
| /// # Safety | ||
| /// | ||
| /// - `r_is_valid` must be assignable. | ||
| /// - `r_is_valid` comes uninitialized and must be set. | ||
| pub unsafe extern "C" fn to_string<T: cap::GodotToString>( |
There was a problem hiding this comment.
Safety sections are the requirements a caller must fulfill, not the requirements on the function implementation (2nd bullet point).
But let's remove it altogether, as only Godot calls it and none of the other callbacks has it. You can still mention the fact that
is_validcomes uninitialized and must be set by this function
as a regular (non-safety) comment.
godot-core/src/registry/callbacks.rs
Outdated
| pub unsafe extern "C" fn to_string<T: cap::GodotToString>( | ||
| instance: sys::GDExtensionClassInstancePtr, | ||
| is_valid: *mut sys::GDExtensionBool, | ||
| r_is_valid: *mut sys::GDExtensionBool, |
There was a problem hiding this comment.
Please revert the rename. While it follows Godot convention, it's inconsistent with all the other callback signatures here. It's even inconsistent with the other parameter in this exact function (would be p_instance in Godot).
- Document when type inference works and when it doesn't. - Explicitly enforce `Trait: 'static` for `DynGd<T, D>` and `AsDyn<D>` – mostly for clarity, creating non-static `DynGd<T, D>` was already pretty much impossible, and `T` – GodotClass – must be 'static either way.
901e79a to
3529708
Compare
Closes: #1026
Trait: 'staticforDynGd<T, Trait>andAsDyn<Trait>(we have no means to track lifetimes of given object either way which means it might be invalidated at any point – albeit creating suchDynGdis hard, sinceGodotClassmust be'staticeither way).Despite messing with it for some time I couldn't force compiler to properly infer given types – thus I limited myself to merely documenting it 😒.