Closed vtenfys closed 5 months ago
@davidbailey00 Latest Neon has the same implementation. It is not possible to implement From<T> for Throw
because it requires a Context
. Thread local storage could be used to store a context, but this would be error prone since it could fail at runtime.
An alternative might be a wrapper that expects a custom error type that has a variant of Throw
.
enum MyError {
SomeError,
OtherError,
Throw,
}
impl From<Throw> for MyError {
fn from(other: Throw) -> Self {
MyError::Throw
}
}
trait ResultExt<T> {
fn or_throw<'a, C: Context<'a>>(self, cx: &mut C) -> NeonResult<T>;
}
impl<T> ResultExt<T> for Result<T, MyError> {
fn or_throw<'a, C: Context<'a>>(self, cx: &mut C) -> NeonResult<T> {
match self {
Ok(v) => Ok(v),
Err(MyError::SomeError) => cx.throw_error("MyError"),
Err(MyError::OtherError) => cx.throw_error("OtherError"),
Err(MyError::Throw) => Err(Throw),
}
}
}
fn my_error_function(mut cx: FunctionContext) -> JsResult<JsString> {
fn my_error_function<'a>(cx: &mut FunctionContext<'a>) -> Result<Handle<'a, JsString>, MyError> {
// The `?` operator can be used in this method with both exceptions and Rust errors
let _ = cx.throw_error("Oh, no!")?;
Ok(cx.string("Hello"))
}
my_error_function(&mut cx).or_throw(&mut cx)
}
The boilerplate of wrapping the real function with a function that calls .or_throw(&mut cx)
could be done with a macro.
How about this? (where Result
is std::result::Result
)
trait OrThrow<T> {
fn or_throw<'a, C: Context<'a>>(self, cx: &mut C) -> NeonResult<T>;
}
impl<T, E: Display> OrThrow<T> for Result<T, E> {
fn or_throw<'a, C: Context<'a>>(self, cx: &mut C) -> NeonResult<T> {
match self {
Ok(v) => Ok(v),
Err(e) => cx.throw_error(e.to_string()),
}
}
}
From the docs on neon::result::Throw
Throw
deliberately does not implementstd::error::Error
, because it's generally not a good idea to chain JavaScript exceptions with other kinds of Rust errors, since entering into the throwing state means that the JavaScript engine is unavailable until the exception is handled.
I'm not sure I understand what the last part of this sentence means, and whether the same is true for my example above? It seems like implementing std::error::Error
for Throw
, or using my code above, would both result in converting a foreign error to a Neon Throw
error (in different ways) - is my understanding correct?
Apologies for lots of questions, I come from a JS background but rather new to Rust and still have lots to learn!
Ah, I realise in my above comment I'm mixing up impl Error for Throw
(which is what the docs refer to) with impl From<Error> for Throw
(which is what would be similar to the .or_throw()
method but doesn't actually exist in the Neon codebase). I think I now understand the reasoning behind removing impl Error for Throw
to avoid accidentally converting JS errors to Rust errors, whereas the code samples above do the opposite. Hopefully my understanding of this is now correct?
I realise now that impl From<Error> for Throw
might not be a good idea either because Throw
alone doesn't include any information about the error - a JsResult
is needed for this to bubble up a meaningful error to JS. That said, perhaps I could write an RFC to include the code in https://github.com/neon-bindings/examples/pull/67#issuecomment-794629880 directly in Neon?
This PR adds a function to the
errors
example which shows how to useor_else
to translate library-specific errors to JavaScript errors.This partially addresses https://github.com/neon-bindings/neon/issues/166 although I'm not sure how to go about implementing
From<CustomError>
(whereCustomError
is an error type created by the author of a Neon project) forneon::result::Throw
or if this is possible. Throwing errors seems to require a context object which isn't available from the requiredfrom
function.So I would like to ask, is it possible to do the above with the latest Neon?