This is a suggestion for adding an exception system to Rust that satisfies these requirements: 1. Unwinding does NOT pass through code that is not either in a function that declares throwing exceptions or in a try block (instead, that triggers task failure) 2. Callers can ignore the fact that a function throws an exception, resulting in task failure 3. Callers can handle exceptions if desired, with the same syntax of C++, Java and C#
(1) avoids the issue with C++ exception handling forcing to think about exception safety everywhere (2) avoids the issue with Java's checked exceptions forcing to explicitly handle or redeclare them (3) provides an easy-to-use exception mechanism The specification is based on adding some syntax sugar, which is then transformed by the compiler into the current Rust syntax, and thus does not really alter runtime behavior (but it is also possible to implement this with traditional unwinding if desired). Functions can be declared to throw exceptions, and if the conceptual unwinding process would have to pass through a function call that does not declare throwing an exception of that type, task failure is invoked instead. To integrate with the condition system, one can raise a condition instead of throwing, and declare the condition type and handlers with "throws", allowing them to throw exceptions if desired (note that of course this will just result in task failure unless the function raising the condition is declared to throw). It is of course possible to code using this style "by hand", but the syntax sugar allows to do it in a much terser way, and allows to convert functions that use task failure to functions that throw exceptions without changing the source code of callers that don't want to handle them. * New syntax added: - "throws E1, E2, ..." modifier on functions and closures - try/catch block - try() expression - throw e statement * New type declarations added: enum Result1<T, E1> { Ok(T), Fail(E1) } enum Result2<T, E1, E2> { Ok(T), Fail1(E1), Fail2(E2) } ... and so on... * Transformations to implement the system: Everywhere: fn foo() -> T throws E1 [similar with closures] => fn foo() -> Result1<T, E1> fn foo() -> T throws E1, E2 [similar with closures] => fn foo() -> Result2<T, E1, E2> try(f(args)) [if f declared with throws] => f(args) f(args) [if f returns T and is declared with throws, not inside try()] => match f(args) {Ok(x) => x, Fail1(e) => throw e, Fail2(e) => throw e, ...} In functions declared throws: return v => return Ok(v) try {try_body} catch(e1: E1) {catch_body} catch(e2: E2) {catch_body} => let mut e1_: Option<E1> = None; let mut e2_: Option<E2> = None; 'try_: loop {try_body; break;} if e1_.is_some() { let e1 = e1_.unwrap(); catch1_body } else if e2_.is_some() { let e2 = e2_.unwrap(); catch2_body } throw e, if there is any containing try block that has a catch block with argument eK with type EK such that e is convertible to EK (taking the innermost suitable try block and the first suitable catch block) => eK_ = Some(e); break 'try_ throw e, if there are no suitable try blocks but the function is declared throws and e is convertible to throws type EK => return FailK(e) throw e, otherwise, if e implements ToStr: => fail!(e.to_str()) throw e, otherwise: => fail!()
_______________________________________________ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev