-
-
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
Opt-in support for functions that return Result<T,GodotError> to indicate failable operations #425
Comments
Thanks a lot for all the thoughts and this detailed proposal! Looks sensible to me 👍 We should probably wait until #421 is merged, as that may change/simplify a few things. There may also be a clearer idea how marshaling errors map to enums. The two changes --
Rather
(Emphasis mine) That can be meaningful for some calls, but it also makes it impossible to handle errors on GDScript side. Basically the same rationale behind this issue -- namely being able to precisely handle errors instead of just panics -- may apply to GDScript, albeit to a lesser extent. |
Sounds good. I'll keep an eye on #421 then. Also, noted on separating PRs. I think it would probably make sense to support Result on
Completely agree. I figured this would require some additional API on the Godot side to be able to report an error with messaging (and maybe even a backtrace if the ext supports it). Does such an API already exist? Or is there an appropriate return value already? In the absence of that I'd just expect to keep the present behavior and treat it like panic! (via the actual panic returned by unwrap_or_else or more structured if possible). |
Not that I'm aware of, unfortunately. There has been some discussion in godotengine/godot-proposals#4736, but no results yet. |
Good point, thanks. I think there are still some changes left to do:
|
Nice, I think I might investigate working on these once Godot 4.3 launches and we start targeting it in main |
There was a recent discussion on Discord with some more insights. There are different views how
This is compounded by the fact that GDScript has no proper error handling, and there are various ways in Godot APIs to work around this ( One of my attempts to sum it up in concrete terms:
# Custom properties, access to wrong one causes error
var ok: Variant
var err: Variant
func is_ok(): bool
func to_godot_error(): Error # OK or FAILED
# maybe more APIs
#[func(on_err=print)]
fn do_sth() -> Result<Gd<Node>, String>
// effectively returns Gd<Node>, but calls godot_print!(err) on Err
#[func(on_err=map_to_error)]
fn do_sth() -> Result<Gd<Node>, String>
// effectively returns global::Error, discards any values in T/E, only considers which one is set Personally, my concern was that passing errors to GDScript that just behave like a panic (i.e. print error and use dummy result) are unrecoverable and should maybe not be thrown in the same bucket as actual |
In order to support Result, we should first define a
GdError
enum that implementsstd::error::Error
. It could replaceengine::global::Error
but I think it would probably make more sense to have our own enum so it's extensible. Ideally this would contain engine error as one option:Some of these
*Details
structs may just be unit types if there's no details available right now (Godot apparently doesn't have a GetLastScriptError API for instance, but would leave the door open for improvement in the future). It may also make sense to make multiple error enums for functions where only some of these are possible (perhaps GdCallError should differ for instance, YMMV) but the idea is the same.Once this is defined, it would be ideal to have a policy of having a function which returns Result whenever an operation is failable. In particular, anything that triggers GDScript (inline script only, deferred script calls need not offer this guarantee) may potentially fail due to a script error and surfacing that failure to calling code is important to be able to enforce invariants. Only the calling code knows whether that error should be propagated or if it's safe to eat it, or perhaps handle it in some custom manner (possibly custom to that particular call site).
To that end, the following calls are proposed to extend the existing interface:
Object::try_call
,Object::try_get
,Object::try_set
Callable::try_callv
PackedScene::try_instantiate(&self)
Object::emit_signal
is inline, it should have a try, but I think it's deferred (not sure).These should all return the same value as the regular non-try function in the
Ok
case andGdError
in theErr
case. To make sure the code paths get exercise, the non-try variant could be implemented in terms of the try_* variant with.unwrap()
.The above changes would allow Rust code to determine when scripts fail. To compliment this, it would also be good to accept Rust functions exposed to Godot that return values of the form
Result<T : ToGodot, E : std::error::Error>
. When called via Godot, this would simply return theOk
type or behave in the same was as though there was a trapped panic (in today's behavior) in the event of anErr
.This seems like trivial behavior, but it has a few advantages:
try_*
error results with the try operator (?
) if it doesn't want to do specific handling.The text was updated successfully, but these errors were encountered: