Implement convenience conversions for Error#1528
Implement convenience conversions for Error#1528CenTdemeern1 wants to merge 2 commits intogodot-rust:masterfrom
Error#1528Conversation
This comment was marked as outdated.
This comment was marked as outdated.
These conversions are primarily intended to help short-circuit Godot's `Error`s. (Revision 2 with documentation adjustments)
|
API docs are being generated and will be shortly available at: https://godot-rust.github.io/docs/gdext/pr-1528 |
| /// | ||
| /// To assist with this, `Result<(), Error>` has [a `ToGodot` implementation](ToGodot#impl-ToGodot-for-Result%3C(),+Error%3E) | ||
| /// that turns it back into an `Error` on Godot's side, so that it can be used as the return value of `#[func]` functions. | ||
| pub fn err(self) -> Result<(), Self> { |
There was a problem hiding this comment.
Wouldn't it make more sense to call this into_result? That would also be consistent with from_result.
There was a problem hiding this comment.
Or just .result()? (see also my main comment about _or/_or_else style fns)
There was a problem hiding this comment.
I wanted to keep it brief because it's meant to be used repeatedly, and I thought it was somewhat similar to Result::err().
There was a problem hiding this comment.
Meant to be used repeatedly when? In what contexts?
I would also opt for result. For me main usecase for mappings is being able to chain it nicely and make it consistent with other consumers, for example:
.map(Gd::try_cast::<Node3D>)
// Semantically – proceed further if casting has been successful.
.map(Result::ok)
.flatten()
.and_then(|n| n.get_world_3d())
.map(|w| w.get_space())with your implementation it would be easy to do something along the lines of:
my_return_value
// Convert to result.
.err()
...
// Convert to option.
.ok()
// if it is ok then we can map it...
.and_then(...)or
my_return_value
// Convert to result.
.err()
...
// Convert to option.
.err()
// if it is err then we must handle it...
.and_then(...)There was a problem hiding this comment.
Thanks for the contribution!
Regarding naming, the standard library has these functions:
Option<T>::ok_or(err: E) -> Result<T, E>
Option<T>::ok_or_else(err_func: F) -> Result<T, E>
Result<T, E>::ok() -> Option<T>
Result<T, E>::err() -> Option<E>Here, we have
Error::err() -> Result<(), Error>which I don't think is a good name.
If we want to add any of the _or/_or_else functions later, we should probably name them all as a group.
I'm against implementing GodotConvert and the other traits on Result<(), Error> before we have a resolution on #425. This has a high chance of conflicting with a future blanket impl for Result, for a very niche feature that can easily be implemented via conversion functions.
| /// Converts this `Error` into a `Result<(), Error>` which is `Ok(())` if the given value is `Error::OK`. | ||
| /// | ||
| /// This is a convenience method that may be used to convert this type into one that can be used with the try operator (`?`) | ||
| /// for easy short circuiting of Godot `Error`s. | ||
| /// | ||
| /// To assist with this, `Result<(), Error>` has [a `ToGodot` implementation](ToGodot#impl-ToGodot-for-Result%3C(),+Error%3E) | ||
| /// that turns it back into an `Error` on Godot's side, so that it can be used as the return value of `#[func]` functions. |
There was a problem hiding this comment.
Too much docs -- the first line is enough, rest could be illustrated with a doctest example instead (a function using ? to return).
There was a problem hiding this comment.
Sure thing, but that would rely on GodotConvert being implemented for Result<(), Error>.
There was a problem hiding this comment.
Why? The conversion between Error and Result<(), Error> via explicit function is pretty stand-alone. It doesn't even need a running engine, so you can make a real doctest that actually asserts the result.
There was a problem hiding this comment.
As I mentioned before, a function needs to be able to return Result<(), Error> for the try operator to work.
| /// Creates an `Error` from a `Result<(), Error>`, the inverse of [`.err()`](Self::err). | ||
| /// | ||
| /// `Ok(())` becomes `Error::OK`, and `Err(e)` becomes `e`. | ||
| pub fn from_result(result: Result<(), Self>) -> Self { | ||
| match result { | ||
| Ok(()) => Error::OK, | ||
| Err(e) => e, | ||
| } | ||
| } |
There was a problem hiding this comment.
What is the use case of from_result()? I don't mean how it's used, but why it is needed.
The Error enum usually comes from engine APIs, so converting it to Result is probably the more typical flow.
There was a problem hiding this comment.
It might be useful if you ever need to call one of your #[func]s that returns Result<(), Error> from the Rust side. Plus, having conversions going both ways seems beneficial.
There was a problem hiding this comment.
Without implementing ToGodot -- which we currently can't do, see main thread -- #[func] cannot return Result<(), Error> though.
Can you elaborate on what you mean by conversion functions? Try blocks aren't stable yet so the only real way to use the try operator in stable Rust is to have the function return I'm not against removing this implementation when the blanket implementation happens, though. |
Removing implies a breaking change, which limits us to minor version releases (and even then, it can still break users' code). And that puts an unnecessary constraint on #425, which is otherwise an additive, backwards-compatible change -- for no functional benefit, just minor convenience. I fully see the convenience around all the |
This breaks the ability to use the try operator, which is the entire crux of this PR. |
|
Ah, fair point. Then I'm afraid we have to either solve #425 or find another way. I do think the to/from |
|
I’ll probably just document the closure hack for now then and remove the |
|
@CenTdemeern1 could you update this PR, so we can merge the non-controversial parts already? 🙂 In the meantime, I'll try to give #425 a shot. |
|
There's now a solution that allows to return Let me know if you're still interested in pursuing this PR. |
|
Hi, sorry, I got wrapped up in a lot of other things. With the current state of things, should I still make the aforementioned PR changes, or do I need to do something with the new Result PR? |
|
This depends a bit. What you originally wanted to achieve:
is now possible with #1544. Is there more we need on top? |
|
I made some changes; let me know if I forgot anything |
It seems I forgot to clean up after myself
|
As mentioned in my previous comment: Before going further into technicals and naming, it would still be good to show what's the use case of the remaining APIs, now that we have general One of the docs mentions "makeshift try blocks", but this doesn't convince me -- it's quite a cumbersome "idiom" and the same might be better achieved with actual try blocks on nightly (which a user should be able to use even if godot-rust does not). So what exactly do we want to achieve now? 🙂 |
This PR implements convenience conversions from
ErrortoResult<(), Error>and back to help with short-circuiting GodotErrors.Conversions
ErrortoResult<(), Error>:Error::err()andFromGodotResult<(), Error>toError:Error::from_result()andToGodotWhy?
Godot's
Erroris already conceptually similar toResult, having anOKandERR_*variants, so being able to use the try operator (?) on them would be convenient.Being able to return
Result<(), Error>from a#[func]also helps with this.Open questions
Result<(), Error>be a type alias, such asErrorResult?Considered alternatives
Trydirectly is nightly-only.