koute / stdweb

A standard library for the client-side Web
Apache License 2.0
3.44k stars 177 forks source link

Establish a pattern for JS exception handling #28

Open Herschel opened 7 years ago

Herschel commented 7 years ago

Lots of web APIs throw exceptions, so we should establish a pattern for passing these exceptions back to Rust in a nice way.

For example, Node::remove_child uses a pattern of returning a bool when an exception happens, which is then converted into a Rust NotFoundErr as appropriate.

I could picture this pattern being wrapped into some kind of js_try! macro. Could the exception itself be serialized to Rust if we implemented the JS Error type? That way, instead of a bool, the JS could return the value itself or the exception, which could be converted into the proper Rust type.

What should the Rust error type be? For example, Node.removeChild can return other exception types. Should we make a more global webapi::Error enum that represents every possible exception that JS can throw?

koute commented 7 years ago

My gut feeling is that having functions return an unified Error type isn't a great idea; a lot of the errors thrown from the JS world are unrelated to each other, so if we unify every error under a single enum we'll lose the very nice property of being able to tell which exact errors a given function can return based only on its return value.

What we could do is to have them return specific types (or single use enums just for that function?), and have an unifying enum which has Into and TryFrom implemented for every concrete error type. Then if the user wants to unify them he easily can.

Another issue (which was iterated in https://github.com/koute/stdweb/issues/21) is that we probably would like the errors to be a little more than a simple struct/enum. JS errors have stack traces, and I would love to fully support that on the Rust side. (This, however, would require some more investigation and work, e.g. for example we'd definitely need some way to decode backtraces to be human readable.)

Anyway, so maybe something like this?

// Corresponds to the JS' Error interface.
trait IError: FromReference {
}

// Corresponds to an instance of a JS object implementing the Error interface.
struct Error( Reference );

impl IError for Error {}

reference_boilerplate! {
    Error,
    instanceof Error
}

// Actual error types.
struct TypeError { backtrace: Backtrace, ... };

// An unifying type.
enum JsError {
    // All of the standard errors.
    TypeError( TypeError ),
    // ....
    // An error that we don't know of, so we store the original reference.
    OtherError( Error )
}
Herschel commented 7 years ago

I agree that it'd be nice to have a function return the specific error types it might throw, although I'm a bit worried about code bloat since this will possibly need an enum-per-throwing-function. I'll give it a shot this week!