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

Reply via email to