-
-
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
Consistent error APIs #536
Comments
Thanks for this, I think that this is great time for this as IMO we could really benefit from good, consolidated structure of
In hindsight, using enum wasn't a very good idea on my part, probably inspired by Godot's error and day-to-day work in PHP with error codes :) To start the topic, as impl
It would require every discriminant of sort to implement I just wonder if if let Some(MySpecificError) = more_general_error.source() {
// ...recover strategy for MySpecificError...
} though this way could also introduce great number of Errors, even simple unit struct types. Eg. as
Other way I see is to go back in time to the integer error codes. This way we can define private enum with integer representation and add codes to it, returning only the integer publicly - though IMO it is even worse than pure enum solution. |
I'm not sure if we should go this route, as it leads to a proliferation of dozens if not hundreds of dedicated types with overly generic names ( Also, I don't see a real use case for this -- we can easily keep this as a single enum and in its
The problem I see here is that this reinvents what a (non-exhaustive) enum does. But instead of having an explicit relation supported by the type system and
I don't think enum is a bad idea per se, but I'm not sure if we should map every internal error 1:1 to our public API. I'm more in favor of abstracting where reasonable. If really needed, we can provide a low-level method returning Godot's integer error constant (this could even be TLDR: I'd like to keep things as simple as possible while still providing the option to differentiate where it makes sense. The less public types, the better. |
I don't have much to add to the discussion, other than that it's sometimes necessary to make a decision based on the error reason, so it's good to not hide it from the logic if the information exists. Exposing the Godot error integers, as mentioned, should be good enough for that, IMO. This is for when a single godot/gdext function may have multiple error reasons and there's no other way to distinguish between them. |
I think there may be value in a runtime/compile time split. I do agree its a very fine line to have and very difficult to discern. The possibility of locking the api into supporting things that are harmful in the future is something I don't see as worthwhile. For developers this makes porting to later versions difficult and for y'all its an additional burden. I can't see benefit for either party. What I would find most beneficial (as someone who really likes to use the Given the "best" response for at least one branch is inevitably graceful exiting. (the developer used the library incorrectly) Perhaps the distinction should be Given all that, I can't say what would be ideal. Only agree with you that this is a very hard problem. With that in mind. I do have an idea. Inverting the paradigm. Perhaps the exposed error types should only be those that are impossible to recover from. The user can check for those and then handle the catch all error case based on their domain. IE: The user looks for a node at the path they have the following cases:
I can't say how practical this is from an implementation standpoint, but I believe its agreeable from a user standpoint. |
imma be real with you, i only found out this existed today when i tried to unwrap the returned value of Also, snippet of stuff I wrote in discord: for the most part i've just been using the panic versions of stuff because usually like i see the purpose for having the |
There are several places in gdext where things can go wrong. At the moment, there is no unified guideline on how to design such APIs.
Logic vs runtime error
Traditionally, errors can be split into logic errors (bugs that must be fixed) and runtime errors (that need to be expected and handled by the developer). However, this is not a very clear-cut line, since the scene tree or other invariants are technically known at compile time, but can still fail.
For example,
try_load::<T>("path/to/Node")
can fail for wrong paths or wrong types. This may be a logic error (assuming the developer should know their scene tree) or a runtime error (if tree is created dynamically, e.g. in a level editor). As such, I'm not sure if strict split of logic/runtime is that useful here.Coarse vs. fine
To me, first priority is that there is a descriptive error message. If something goes wrong, the developer should have a way to know this from the error message, ideally with some context.
Then, there has been the discussion about mapping each internal error to a public enumerator. That would mean most APIs have their own domain-specific error types. But how do we design those, if they have more fine-grained "internal" causes that may not be relevant for the public API?
I would like the design be driven by actual use cases and not theoreticals. Yes, it's possible to expose all information that gdext possesses about an error to the user, but it's also costly and impractical, in terms of maintenance, discoverability, good abstraction boundary, UX. So each symbol that is exposed must have a clear use case.
Integration with Godot
There is currently
engine::global::Error
which is returned from some engine APIs. We also have domain-specific errors about failed calls, etc. Ideally we can integrate them into our error API design.Compatibility
Having very fine-grained error enums comes at a cost, even with
#[non_exhaustive]
. It makes it harder to refactor things, smaller changes often become breaking. For example, changing an error enum from unit (C-style enumerator) to struct style (having fields).Compatibility also conflicts with fine-grainedness. Once we have crates.io releases and take SemVer seriously, we cannot easily break these things without waiting for the next release, and it's not great if error types are expressing the wrong thing due to technical limitations (like adding a field that would interfere with existing code).
Self-containedness
I don't want to add crates like
thiserror
oranyhow
to gdext. But maybe we can design errors in a way that they are extensible and composable with these crates.The text was updated successfully, but these errors were encountered: