Skip to content
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

Virtual methods must take Option<Gd<T>> instead of Gd<T> #494

Closed
andreymal opened this issue Nov 22, 2023 · 23 comments · Fixed by #883
Closed

Virtual methods must take Option<Gd<T>> instead of Gd<T> #494

andreymal opened this issue Nov 22, 2023 · 23 comments · Fixed by #883
Labels
bug c: register Register classes, functions and other symbols to GDScript
Milestone

Comments

@andreymal
Copy link
Contributor

I'm trying to make an editor plugin:

use godot::engine::IEditorPlugin;
use godot::prelude::*;

#[derive(GodotClass)]
#[class(tool, editor_plugin, init, base=EditorPlugin)]
pub struct MyEditorPlugin {}

#[godot_api]
impl IEditorPlugin for MyEditorPlugin {
    fn handles(&self, _: Gd<Object>) -> bool {
        true
    }

    fn edit(&mut self, object: Gd<Object>) {}
}

For some reason object's type is non-optional Gd<Object>, but the documentation says:

object can be null if the plugin was editing an object, but there is no longer any selected object handled by this plugin. It can be used to cleanup editing state.

As a result, when no node is selected, Godot tries to call ._edit(null) and crashes.

Backtrace if needed
thread '<unnamed>' panicked at /home/andreymal/.cargo/git/checkouts/gdext-76630c89719e160c/13ab375/godot-core/src/builtin/meta/signature.rs:450:5:
edit: parameter [0] has type godot_core::obj::gd::Gd<godot_core::gen::classes::object::re_export::Object>, which is unable to store argument "TODO"
stack backtrace:
   0:     0x7fb90fde422c - std::backtrace_rs::backtrace::libunwind::trace::h67a838aed1f4d6ec
                               at /rustc/79e9716c980570bfd1f666e3b16ac583f0168962/library/std/src/../../backtrace/src/backtrace/libunwind.rs:93:5
   1:     0x7fb90fde422c - std::backtrace_rs::backtrace::trace_unsynchronized::h1d1786bb1962baf8
                               at /rustc/79e9716c980570bfd1f666e3b16ac583f0168962/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5
   2:     0x7fb90fde422c - std::sys_common::backtrace::_print_fmt::h5a0b1f807a002d23
                               at /rustc/79e9716c980570bfd1f666e3b16ac583f0168962/library/std/src/sys_common/backtrace.rs:67:5
   3:     0x7fb90fde422c - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::hf84ab6ad0b91784c
                               at /rustc/79e9716c980570bfd1f666e3b16ac583f0168962/library/std/src/sys_common/backtrace.rs:44:22
   4:     0x7fb90fe07fec - core::fmt::rt::Argument::fmt::h28f463bd1fdabed5
                               at /rustc/79e9716c980570bfd1f666e3b16ac583f0168962/library/core/src/fmt/rt.rs:138:9
   5:     0x7fb90fe07fec - core::fmt::write::ha37c23b175e921b3
                               at /rustc/79e9716c980570bfd1f666e3b16ac583f0168962/library/core/src/fmt/mod.rs:1114:21
   6:     0x7fb90fde1ebe - std::io::Write::write_fmt::haa1b000741bcbbe1
                               at /rustc/79e9716c980570bfd1f666e3b16ac583f0168962/library/std/src/io/mod.rs:1763:15
   7:     0x7fb90fde4014 - std::sys_common::backtrace::_print::h1ff1030b04dfb157
                               at /rustc/79e9716c980570bfd1f666e3b16ac583f0168962/library/std/src/sys_common/backtrace.rs:47:5
   8:     0x7fb90fde4014 - std::sys_common::backtrace::print::hb982056c6f29541c
                               at /rustc/79e9716c980570bfd1f666e3b16ac583f0168962/library/std/src/sys_common/backtrace.rs:34:9
   9:     0x7fb90fde5783 - std::panicking::default_hook::{{closure}}::h11f92f82c62fbd68
                               at /rustc/79e9716c980570bfd1f666e3b16ac583f0168962/library/std/src/panicking.rs:272:22
  10:     0x7fb90fde54a4 - std::panicking::default_hook::hb8810fe276772c66
                               at /rustc/79e9716c980570bfd1f666e3b16ac583f0168962/library/std/src/panicking.rs:292:9
  11:     0x7fb90fde5e81 - <alloc::boxed::Box<F,A> as core::ops::function::Fn<Args>>::call::h87b887549356728a
                               at /rustc/79e9716c980570bfd1f666e3b16ac583f0168962/library/alloc/src/boxed.rs:2021:9
  12:     0x7fb90fde5e81 - std::panicking::rust_panic_with_hook::hd2f0efd2fec86cb0
                               at /rustc/79e9716c980570bfd1f666e3b16ac583f0168962/library/std/src/panicking.rs:735:13
  13:     0x7fb90fde5c01 - std::panicking::begin_panic_handler::{{closure}}::h3651b7fc4f61d784
                               at /rustc/79e9716c980570bfd1f666e3b16ac583f0168962/library/std/src/panicking.rs:609:13
  14:     0x7fb90fde4756 - std::sys_common::backtrace::__rust_end_short_backtrace::hbc468e4b98c7ae04
                               at /rustc/79e9716c980570bfd1f666e3b16ac583f0168962/library/std/src/sys_common/backtrace.rs:170:18
  15:     0x7fb90fde5952 - rust_begin_unwind
                               at /rustc/79e9716c980570bfd1f666e3b16ac583f0168962/library/std/src/panicking.rs:597:5
  16:     0x7fb90fc0ed25 - core::panicking::panic_fmt::h979245e2fdb2fabd
                               at /rustc/79e9716c980570bfd1f666e3b16ac583f0168962/library/core/src/panicking.rs:72:14
  17:     0x7fb90fc370e0 - godot_core::builtin::meta::signature::param_error::h9e5e0410d0fe12c0
                               at /home/andreymal/.cargo/git/checkouts/gdext-76630c89719e160c/13ab375/godot-core/src/builtin/meta/signature.rs:450:5
  18:     0x7fb90fc37b46 - godot_core::builtin::meta::signature::ptrcall_arg::{{closure}}::h3a978076abd52718
                               at /home/andreymal/.cargo/git/checkouts/gdext-76630c89719e160c/13ab375/godot-core/src/builtin/meta/signature.rs:430:41
  19:     0x7fb90fc268dc - core::option::Option<T>::unwrap_or_else::h235c3edeffbe6d51
                               at /rustc/79e9716c980570bfd1f666e3b16ac583f0168962/library/core/src/option.rs:979:21
  20:     0x7fb90fc37afc - godot_core::builtin::meta::signature::ptrcall_arg::hda9ba436188e0501
                               at /home/andreymal/.cargo/git/checkouts/gdext-76630c89719e160c/13ab375/godot-core/src/builtin/meta/signature.rs:430:5
  21:     0x7fb90fc3d3fd - <(R,P0) as godot_core::builtin::meta::signature::PtrcallSignatureTuple>::in_ptrcall::hd190a75f79badd54
                               at /home/andreymal/.cargo/git/checkouts/gdext-76630c89719e160c/13ab375/godot-core/src/builtin/meta/signature.rs:272:30
  22:     0x7fb90fc3612d - <my_project::MyEditorPlugin as godot_core::obj::traits::cap::ImplementsGodotVirtual>::__virtual_call::function::hbdcacccc161356a7
                               at /home/andreymal/projects/my_project/src/lib.rs:67:1
  23:     0x55e9ee942dec - _ZN12EditorPlugin21_gdvirtual__edit_callILb0EEEbP6Object
                               at /usr/src/debug/godot/godot-4.1.3-stable/editor/editor_plugin.h:86:2
  24:     0x55e9ee942dec - _ZN12EditorPlugin4editEP6Object
                               at /usr/src/debug/godot/godot-4.1.3-stable/editor/editor_plugin.cpp:320:2
  25:     0x55e9ee8e6ac0 - _ZN10EditorNode19hide_unused_editorsEPK6Object
                               at /usr/src/debug/godot/godot-4.1.3-stable/editor/editor_node.cpp:2237:21
  26:     0x55e9eec441b8 - _ZN13SceneTreeDock18_selection_changedEv
                               at /usr/src/debug/godot/godot-4.1.3-stable/editor/scene_tree_dock.cpp:2271:13
  27:     0x55e9f1d4f25b - _ZN6Object12emit_signalpERK10StringNamePPK7Varianti
                               at /usr/src/debug/godot/godot-4.1.3-stable/core/object/object.cpp:1070:20
  28:     0x55e9ef67df96 - _ZN6Object11emit_signalIJEEE5ErrorRK10StringNameDpT_
                               at /usr/src/debug/godot/godot-4.1.3-stable/./core/object/object.h:884:22
  29:     0x55e9ef67df96 - _ZN15EditorSelection12_emit_changeEv
                               at /usr/src/debug/godot/godot-4.1.3-stable/editor/editor_data.cpp:1292:13
  30:     0x55e9ed92430d - _Z29call_with_variant_args_helperI17__UnexistingClassJEJEEvPT_MS1_FvDpT0_EPPK7VariantRN8Callable9CallErrorE13IndexSequenceIJXspT1_EEE
                               at /usr/src/debug/godot/godot-4.1.3-stable/./core/variant/binder_common.h:303:25
  31:     0x55e9ed92430d - _Z25call_with_variant_args_dvI17__UnexistingClassJEEvPT_MS1_FvDpT0_EPPK7VariantiRN8Callable9CallErrorERK6VectorIS7_E
                               at /usr/src/debug/godot/godot-4.1.3-stable/./core/variant/binder_common.h:450:31
  32:     0x55e9ed92430d - _ZNK11MethodBindTIJEE4callEP6ObjectPPK7VariantiRN8Callable9CallErrorE
                               at /usr/src/debug/godot/godot-4.1.3-stable/./core/object/method_bind.h:331:28
  33:     0x55e9f1d4527b - _ZN6Object5callpERK10StringNamePPK7VariantiRN8Callable9CallErrorE
                               at /usr/src/debug/godot/godot-4.1.3-stable/core/object/object.cpp:739:21
  34:     0x55e9f1a5db9f - _ZNK8Callable5callpEPPK7VariantiRS0_RNS_9CallErrorE
                               at /usr/src/debug/godot/godot-4.1.3-stable/core/variant/callable.cpp:62:30
  35:     0x55e9f1d3b930 - _ZN9CallQueue14_call_functionERK8CallablePK7Variantib
                               at /usr/src/debug/godot/godot-4.1.3-stable/core/object/message_queue.cpp:219:18
  36:     0x55e9f1d3f0f0 - _ZN9CallQueue5flushEv
                               at /usr/src/debug/godot/godot-4.1.3-stable/core/object/message_queue.cpp:320:20
  37:     0x55e9efb184fa - _ZN9SceneTree15physics_processEd
                               at /usr/src/debug/godot/godot-4.1.3-stable/scene/main/scene_tree.cpp:471:38
  38:     0x55e9ed7f6c08 - _ZN4Main9iterationEv
                               at /usr/src/debug/godot/godot-4.1.3-stable/main/main.cpp:3390:60
  39:     0x55e9ed780340 - _ZN11OS_LinuxBSD3runEv
                               at /usr/src/debug/godot/godot-4.1.3-stable/platform/linuxbsd/os_linuxbsd.cpp:912:22
  40:     0x55e9ed76fc98 - main
                               at /usr/src/debug/godot/godot-4.1.3-stable/platform/linuxbsd/godot_linuxbsd.cpp:74:9
  41:     0x7fb915358cd0 - <unknown>
  42:     0x7fb915358d8a - __libc_start_main
  43:     0x55e9ed77d255 - _start
  44:                0x0 - <unknown>
fatal runtime error: failed to initiate panic, error 5

I don't know if this is a Godot bug or godot-rust bug, but since I ran into this issue during playing with Rust, I'm leaving this issue here.

@lilizoey
Copy link
Member

seems related to #156

@Bromeon
Copy link
Member

Bromeon commented Nov 23, 2023

Yep in fact it's already tracked by #156. The current workaround is also explained there 🙂

@Bromeon Bromeon closed this as not planned Won't fix, can't repro, duplicate, stale Nov 23, 2023
@Bromeon Bromeon added the status: duplicate This issue or pull request already exists label Nov 23, 2023
@andreymal
Copy link
Contributor Author

andreymal commented Nov 23, 2023

@Bromeon that workaround is not applicable here because I can't control how Godot calls functions.

This means it's currently not possible to make an editor plugin in Rust :(

@Bromeon
Copy link
Member

Bromeon commented Nov 23, 2023

Hm good point. Maybe we should track it separately, in case the two things are not fixed at the same time.
I'll edit the description of the other issue to highlight this.

@Bromeon Bromeon reopened this Nov 23, 2023
@Bromeon Bromeon added bug c: register Register classes, functions and other symbols to GDScript and removed status: duplicate This issue or pull request already exists labels Nov 23, 2023
@Bromeon
Copy link
Member

Bromeon commented Nov 23, 2023

An issue is that the GDExtension API does not expose the information whether a parameter or return type can be null or not. That means we either have to conservatively assume everything can be Option<Gd<T>>, which turns the entire API into a billion-dollar mistake; or we manually go through the process of maintaining this information somewhere, which is a huge effort.

I opened a proposal ages ago, maybe you can upvote it. It is a bit more ambitious, but nullability might already be a good first step:
godotengine/godot-proposals#2550

@StatisMike
Copy link
Contributor

@Bromeon I'm not that knowledgeable about inner workings of Gd, what is the implication of using Option<Gd> instead of Gd for selected builtin method signatures? By no means I'm having in mind compromising null safety by cluttering gdext API with Option<Gd>, but maybe procuring some kind of special cases until upstream solves the issue could be a viable option? Seeing as your issue was posted on godot proposals in 2021 I'm worried it may not be adressed soon, and it is possible that it will gatekeep many very useful features, like the whole IEditorPlugin impl in this case. Which I think is really one of special cases, as it is called within the Godot Editor and its the whole point of that class.

I'm asking about the implications to assess the danger of reverse occuring: I mean Godot's non-nullable marked as nullable on gdext side in case of upstream changes.

@Bromeon
Copy link
Member

Bromeon commented Dec 12, 2023

@StatisMike yes, that's exactly the plan.

I'm working on a proposal that would combine this with an other feature, impl-conversions. At the moment, a lot of arguments need to be passed as "hello".into(), which could be avoided by something like impl Into<StringName>. The idea here would be similar, that a user could pass &Gd, Gd or Option<Gd> for ergonomics. There is some prior art in gdnative::object::AsArg.


Seeing as your issue was posted on godot proposals in 2021 I'm worried it may not be adressed soon

No worries, I (among others) brought it up with the GDExtension team again. We would all benefit from a real solution on Godot side here, and I agree we shouldn't wait for it before we take action on Rust side.

Coincidentally, in the same minute you posted your reply, Godot devs opened godotengine/godot#86079 😀

@Mercerenies
Copy link
Contributor

Happy to see this is being worked on (by the Godot folks). I just ran into the same problem: Many of the ScriptLanguageExtension API methods take an Object*, and they vary on whether or not the argument can be null. This really does need to be documented somewhere upstream that we can consume.

@StatisMike
Copy link
Contributor

@Bromeon I've got the same message for EditorInspectorPlugin::parse_property() - the Gd<Object> can be null, so the signature there is also not correct. I don't think it warrants a new Issue to be opened, just leaving breadcrumb there.

If I find any more of these, I can edit this message to keep track of them but not bloat this comment thread.

@Bromeon
Copy link
Member

Bromeon commented Jul 24, 2024

Yes, I think we have to conservatively change all object parameters in virtual functions to Option<Gd<T>>. It's annoying, but without nullability information our hands are tied.

@StatisMike
Copy link
Contributor

@Bromeon For now it seems to be a few functions affected - I think making every Gd<T> an Option in virtuals can be a little too much?

Maybe a special cases could be made there? Or just only Gd<Object> signatures (as above only these were reported)?

@Mercerenies
Copy link
Contributor

@StatisMike This issue is blocking any attempt to use those functions right now. I'd rather have several functions that always return a Some than a bunch of functions that are literally unusable due to low-level segfaults. The former is solved by a downstream unwrap; the latter can't be solved by downstream libraries and apps.

@Bromeon
Copy link
Member

Bromeon commented Jul 26, 2024

@StatisMike But who maintains the list of functions that are nullable?

I have basically two options:

  1. Switch between Option<Gd<T>> and Gd<T>. Whether it's black- or whitelist based, the effort needs to be done manually. If we forget a nullable parameter, this becomes a bug that now requires a breaking change in the API. And it's actually worse, it's perfectly within Godot's right to never return null until they do, and we have no way of learning about this.

  2. Conservatively stick to Option<Gd<T>>. It's annoying to unwrap, I agree -- but like @Mercerenies says, this can simply be adjusted by the user side, and there's no scenario where we prevent the user from using virtual functions (like today).

I don't like pushing the responsibility to the user, but I don't think we have much choice?

We can try to experiment with a best-effort approach (whitelist based: everything is null unless stated otherwise), but I'd need to crowdsource this effort, as I'm not familiar with all the Godot APIs. Do you have a starting point here?

@StatisMike
Copy link
Contributor

@Bromeon I think we can begin with the ones mentioned in this thread. I will try to spot some differences in godot source code with EditorPlugin and EditorInspectorPlugin implementations -maybe it will be discernable that these methods can receive null.

Most if not all of these virtual methods already have some implementation in Godot's engine. I'm not cpp expert by any chance, but I presume it needs to be handled somehow.

If no, maybe the better call will be to list the special cases per class, not method basis: so all virtual methods intaking Gd<T> in given class will be changed to Option<Gd<T>>.

If we forget a nullable parameter, this becomes a bug that now requires a breaking change in the API.

It seems that all methods that can receive a nullable parameter in the current state are unusable, so I wouldn't worry about breaking change in their implementation. TBH I'm more concerned about breaking change that the global change in all virtual methods would bring.

@Mercerenies What would you think about these? Do you deem doable tracking down the methods in ScriptLanguageExtension API which can receive null, or the approach of targeting whole classes would be better?

@Bromeon
Copy link
Member

Bromeon commented Jul 27, 2024

We should still make the default Option<Gd<T>>, and then explicitly list cases where we can assume non-null Gd<T> (whitelist). This way, the worst that can happen is that users need extra .unwrap(). But the other way around, Godot introducing new APIs may cause new bugs.

I know it's a lot of effort, that's why I was skeptical in the first place -- but simply choosing the easy path means that we may miss a lot of APIs where our assumptions are incorrect, and this is unfixable by the user.

We can consider doing wildcards on a class level if that helps, but I'm not sure if the condition "multiple virtual methods will behave the same within a class" is true? If the parameter doesn't refer to an object of the own type especially, why do you think we can assume this?

Breaking change all over is too bad, but it's a bug and will be done in v0.2. I would encourage users who know that certain parameters are non-null to speak up, so we can keep things non-breaking wherever possible.

@Bromeon Bromeon added this to the 0.2 milestone Jul 27, 2024
@ICEFIR
Copy link

ICEFIR commented Jul 28, 2024

making the default Option<Gd<T>> does make sense as its way more defensive, but i do wonder how we'll handle Godot's method hints once nullability is fully implemented? They seems to already started adding nullability (as seen in godotengine/godot#90767)

My concern is this: if nullability is introduced, will we need to explicitly mark every method we know to be non-nullable as Option<Gd<T>>? This approach seems to disregard the nullability method hints?

Though be it white or blacklist, either approach would be better then what we have at the moment :)

as for the list of functions, the godot doc repo should be a pretty good starting point? a quick doc repo search returns a bunch of situations that a null might be returned.

Btw I am able to work around the issue using a bridging call from GDScript. It's not ideal nor safe, but it does unblock my work. I am NOT recommending this approach, but if you just wants to unblock yourself, Here's what I've implemented:

On the Rust side, I'm using a global variable and a static function:

static mut EDITOR_PLUGIN_INSTANCE: Option<*mut MyEditorPlugin> = None;

#[func]
fn update_object(object: Option<Gd<Object>>) {
    if let Some(object) = object {
        if object.is_class(GString::from("TileSet")) {
            let tile_set = object.cast::<TileSet>();
            unsafe {
                if let Some(instance) = EDITOR_PLUGIN_INSTANCE {
                    (*instance).tile_set_changed(tile_set);
                    godot_print!("TileSet updated");
                } else {
                    godot_error!("instance not found");
                }
            }
        }
    }
}

fn tile_set_changed(&mut self, tile_set: Gd<TileSet>) {
    self.tile_set = Some(tile_set);
    godot_print!("TileSet changed");
}

And on the GDScript side:

func _edit(object) -> void:
    if object == null:
        return
    print("edit triggered, pass object to gdextension")
    MyEditorPlugin.update_better_terrain_faster_object(object)

Obviously this kind of bridging is not ideal for multiple of reasons. I was hoping that i will be able to acquire the plugin instance from gdscript directly but, it appears that the necessary functions (get_editor_plugin and get_editor_plugin_count) are not exposed from godot engine, forcing me to implement the solution on the GDExtension side, which is far from ideal.

@Bromeon
Copy link
Member

Bromeon commented Jul 28, 2024

My concern is this: if nullability is introduced, will we need to explicitly mark every method we know to be non-nullable as Option<Gd<T>>? This approach seems to disregard the nullability method hints?

Definitely not, we would of course use this information as soon as GDExtension exposes it.

But the point is:

  • It currently doesn't expose this info.
  • Even once it does, it will only do so for newer Godot versions (>= 4.4 at least), meaning people supporting older versions won't have nullability information.

as for the list of functions, the godot doc repo should be a pretty good starting point? a quick doc repo search returns a bunch of situations that a null might be returned.

Hm, good idea! 💡

It seems like the docs generally provide info when something is null, not the opposite. So we can't safely say that a parameter is non-null, it might just not be documented 🤔 so maybe we should really start some crowdsourcing approach, and encourage people to contribute functions they regularly use and are known to be non-null.

One option is also to run the in-progress PRs and export nullability info from there, then hardcode that. I don't know how far those have progressed yet, so it might not yet be possible to (reliably) export anything. I'll also check with Godot developers...

@ICEFIR
Copy link

ICEFIR commented Jul 28, 2024

it will only do so for newer Godot versions (>= 4.4 at least), meaning people supporting older versions won't have nullability information.

Good point!

we can't safely say that a parameter is non-null

Indeed. I've been thinking - since for most situations we don't care when the input parameter is null, we could potentially mark all functions as taking Option<Gd> and use a macro to handle the different cases:

If Option.None, it would result in a no-op.
If data exists, it would transform it into Gd<T>.

This should be relatively straightforward to implement. If users want to handle null cases themselves, they could opt out of the macro. Or we create two different version of the #[func] macro, one auto handles null and another pass Option<Gd<T>>. But that's just implementation detail, and you get the drill :)

If done correctly this wouldn't even break the backward compatibility

There might be edge cases where a function takes multiple parameters and only one of them could be null. But, at the end of the day, users can still choose to use the Option<Gd> approach if they prefer.

so maybe we should really start some crowdsourcing approach

Second that!

@Bromeon
Copy link
Member

Bromeon commented Jul 28, 2024

If Option.None, it would result in a no-op.
If data exists, it would transform it into Gd.

This already exists as a function: Option::map 🙂

I don't think we should silently do nothing on the library side, if the parameter is null. This is almost always a bad idea, because it hides logic errors and makes things harder to debug.

@ICEFIR
Copy link

ICEFIR commented Jul 29, 2024

This already exists as a function

I always hesitant to use map for this purpose as, from functional programming perspective map isn't supposed to have side effects. But maybe i am being too pedantic.

besides what i was thinking was more of a higher order function, something along the line of

([a] -> b) -> [Maybe a] -> Maybe b 

which transform a function that doesn't deal with nullable params into one that handles them. But i digress

hides logic errors

Not too sure if i'd agree given i am mainly referring to engine calls, and this is just a default catch-all case that we still apply special case treatment on top, and will use nullability information once GDExtension exposes it. But i do not have too strong of an opinion on this, nor do i claim to be an expert in this area ;)

crowdsourcing

So far what we've found in this thread

EditorPlugin::_edit
EditorPlugin::_handles <- this takes in an Object*

EditorInspectorPlugin::parse_begin
EditorInspectorPlugin::parse_category
EditorInspectorPlugin::parse_group
EditorInspectorPlugin::parse_property
EditorInspectorPlugin::parse_end

and a quick look at ScriptLanguageExtension there's a bunch of function there that takes in pointers.... unfortunately i am not familiar enough with godot code base to call out what actually can be null, so someone needs to fill in here.

I will update back if i discover more

@Bromeon
Copy link
Member

Bromeon commented Jul 29, 2024

I always hesitant to use map for this purpose as, from functional programming perspective map isn't supposed to have side effects. But maybe i am being too pedantic.

There's Option::inspect(), stabilized in Rust 1.76.


So far what we've found in this thread

Thanks! Yeah if we can collect the methods that's great, I could integrate them into the library 🙂

@Bromeon Bromeon changed the title Incorrect signature for EditorPlugin::edit Virtual methods must take Option<Gd<T>> instead of Gd<T> Sep 2, 2024
@fpdotmonkey
Copy link
Contributor

  • Even once it does, it will only do so for newer Godot versions (>= 4.4 at least), meaning people supporting older versions won't have nullability information.

I wonder if Godot devs wouldn't be interested in backporting such a feature. It'd certainly be nice, though I know that the policy is that engine users should keep up with minor versions.

@Bromeon
Copy link
Member

Bromeon commented Sep 2, 2024

Good point, but typically the backporting is for 1-2 minor versions. But in practice, most people aren't further behind than that, either 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug c: register Register classes, functions and other symbols to GDScript
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants