-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Replace Result<T, JsValue> with Result<T, Error> #1742
Comments
This is somewhat similar to #1017 and #1004 as well I think. I'd love to start immediately making progress on this if we can instead of delaying it to a batch of breaking changes though, that'd help us get our feet wet and experiment with with possible ideas before we set it all in stone. One thing we might want to do to start out is to support returning custom error types, converting them with |
That will have bad stack traces though. Because of the way errors work in JS, you basically always want to create the However, a custom error type which contains a Stdweb already does this extensively. It has attributes and macros to generate the error types, and it uses them in the APIs. For web-sys, we should add in a Ideally we would want more fine-grained errors for web-sys, like this: #[wasm_bindgen]
extern "C" {
#[wasm_bindgen(
extends = DomException,
extends = Error,
instanceof = InvalidCharacterError::instanceof
)]
type InvalidCharacterError;
}
impl InvalidCharacterError {
fn instanceof(val: &JsValue) -> bool {
if let Some(exception) = val.dyn_ref::<DomException>() {
exception.name() == "InvalidCharacterError"
} else {
false
}
}
} But the problem is that WebIDL doesn't specify which errors each API throws, so we would need to manually add those in. And it may not even be worth it, since |
Oh, I think I understand what you mean about custom error types. You mean something like this: impl<E> From<E> for js_sys::Error where E: std::error::Error {
#[inline]
fn from(value: E) -> js_sys::Error {
js_sys::Error::new(&value.to_string())
}
} And then it would be possible to use I think that's a good idea. The stack traces might not always be 100% ideal, but they'll be pretty good. |
Yes I don't think that capturing stack traces with the higest of fidelity is that important. Additionally while changing all of web-sys might be nice I think it's fine to leave that for later. For now I'd just like to explore avenues to unlock more functionality today rather than waiting on a bunch of changes to happen "eventually" |
Not sure if it's just me, but it doesn't seem like it's currently possible to construct a
|
@cormacrelf I think you may be running into a separate issue that's too eagerly attempting to print out that a function threw an error, mind opening up a new issue for that? |
I like the idea of having some sort of Right now it's a papercut to try to use an error crate like Anyhow. Because of the orphan rule, a user crate can't implement From for JsValue as would be required for ergonomic error handling. |
Adding Currently working around the lack of Is there a better way? Been doing rust for all of 2 days. #[cfg(target_arch = "wasm32")]
macro_rules! jserr {
($expression:expr) => {
match $expression {
Ok(a) => Ok(a),
Err(e) => {
return Err(JsValue::from(format!("{}", e)));
}
}
};
}
// wasm only. wasm doesn't respect
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
impl Project {
#[wasm_bindgen]
pub fn new(zipdata: Vec<u8>) -> Result<Project, JsValue> {
jserr!(Project::from_zipdata(zipdata))
}
}
// Non-wasm functions. Crappy overloading.
#[cfg(not(target_arch = "wasm32"))]
impl Project {
pub fn new(zipdata: Vec<u8>) -> Result<Project, anyhow::Error> {
Project::from_zipdata(zipdata)
}
}
// Shared, non-accessible wasm functions.
impl Project {
fn from_zipdata(zipdata: Vec<u8>) -> Result<Project, anyhow::Error> {
let mut archive = zip::ZipArchive::new(Cursor::new(zipdata))?;
let file = archive.by_name("foo.json")?;
let mut ds = serde_json::Deserializer::from_reader(file);
Project::deserialize(&mut ds)?
}
} Another issue here is that |
I'm working on a fairly large WASM project, and error handling is rough. Whenever I call a 'rust' program, I have to convert errors into JsValues. This issue would make error handling so much more ergonomic. |
As a side note, it would be nice to update https://rustwasm.github.io/wasm-bindgen/reference/types/result.html with the recommendation to use |
Just wanted to mention that this is also my biggest problem working with web-sys. All my JS-calling Rust functions currently return If anyone has a band-aid solution for this that works right now, please let me (and the others suffering for this) know! OTOH, one of the problems I have is that fn json_error(js_error: JsValue) -> JsValue {
let err: Error = js_error.into();
JsValue::from(format!(
"payload is not valid JSON: {:?}", err.to_string()))
} Not sure if there's a better way (what if |
I saw yesterday that V8 release 9.5 added support for WebAssembly Exception Handling. Is there a way for wasm-bindgen to take advantage of that now? |
Latest update is that @alexcrichton and I have been working on #2710, and we can hopefully merge it pretty soon. It offers a pretty broad-spectrum improvement. Aside from the stack space issue, it extends the range of It also ships a very simple
#[wasm_bindgen]
pub fn throw_an_error() -> Result<i32, JsError> {
subroutine()?;
Err(JsError::new("error message"))
} Of course you do not have to use I think that addresses most of what we need, and also does not constitute a breaking change. You can still throw arbitrary |
I'm going to close this having largely been implemented in #2710, thanks @cormacrelf for all your work implementing this! As mentioned in #2710 (comment) some docs probably need to get updated, but I think it's fine to track that separately. |
I'm also running into some headaches because I use Would it make sense to add an |
Definitely cut myself on this one. Solution was: fn_that_returns_an_anyhow_result().map_err(|err| JsError::new(&err.to_string())) |
Motivation
As discussed in #1735, it's not idiomatic to throw JS values, like
throw "foo"
, because that loses stack traces.Instead it's highly recommended to always use
throw new Error("foo")
. In wasm-bindgen, this corresponds toErr(js_sys::Error::new("foo"))
Internally, wasm-bindgen itself follows this recommendation, however, because the wasm-bindgen (and js-sys and web-sys) APIs return
Result<T, JsValue>
, this encourages user functions to returnResult<T, JsValue>
as well, which makes it easy for users to return non-Error
values:Proposed Solution
All wasm-bindgen APIs which return
Result<T, JsValue>
should instead returnResult<T, Error>
.This includes
wasm_bindgen(catch)
(which should have adebug_assert
to verify that it actually is anError
object)This uses the static type system to make it impossible to accidentally throw non-
Error
types. It also is more "Rust-like", which makes the APIs easier to understand.This is obviously a very large breaking change.
Alternatives
Do nothing. This is a lot less work, and isn't a breaking change, but it does mean that wasm-bindgen is encouraging bad behavior.
Keep
Result<T, JsValue>
, but put in runtime checks which verify that theErr
is ajs_sys::Error
. This is also a lot less work, but it does have a small runtime cost, and it's surprising behavior for the user.The text was updated successfully, but these errors were encountered: