On 25/04/2013 5:29 PM, Brian Anderson wrote:

It's almost taboo here, but we could consider adding catchable exceptions.

For reference sake on this taboo -- let it not be too opaque! -- I will point to the amazing powers of IRC logging, and the conversation that contains the memorable phrase:

  <graydon> please remind me of these things next time someone asks
            us about "resumable exceptions"; I had forgotten how
            convinced I was of this last time through the design-space

http://irclog.gr/#show/irc.mozilla.org/rust/381023

In particular, to summarize for the impatient: once you get resumable exceptions, your code can only be correct if it leaves every data structure that might persist through an unwind-and-catch (that it acquired through &mut or @mut or the like, and live in an outer frame) in an internally-consistent state, at every possible exception-point.

I.e. you have to write in transactional/atomic-writes style in order to be correct. This is both performance-punitive and very hard to get right. Most C++ code simply isn't correct in this sense. Convince yourself via a quick read through the GotWs strcat linked to:

http://www.gotw.ca/gotw/059.htm
http://www.gotw.ca/gotw/008.htm

Now granted, we don't have 'operator=' overloading, so some quantity of this is exaggerated. But not much. Any nontrivial sequence of writes and other-operations winds up requiring transactional treatment if you have resumable exceptions. This is a big tax.

If we stay away from that feature, we get to hold on to sequential logic. Your can reason about state strictly in terms of "the sequence of operations as-written", or prefixes thereof. You never have to think about "someone might call another method on this object after it was half-modified and unwound mid-operation".

I was only thinking that instead of

condition! { io_error: super::IoError -> (); }

you might have

condition! { file_not_found_error: super::FileNotFoundError -> Foo; }
condition! { file_permission_error: super::FilePermissionError -> Bar; }

and so forth. In other words, instead of the branching in the IoError
enum, branching at the condition and error-describing-type level. That
only makes sense in the as-you-would-expect-a-condition-system-to-work
scenario though.

This is attractive, but how could you then create a block of code that
trapped *all* errors?

I would be _totally_ amenable to enhancing the condition-declaring system to provide a means of chaining the default of a condition into raising a different one at the declaration-site. We have to do some further hacking on the condition-declaring system anyways, to handle passing & properly.

At the moment, you _could_ do this if you were fastidious about it at the raise sites. Namely:

  condition! { file_not_found_error: (Path) -> File; }
  condition! { general_file_error: (Path, File) -> File; }
  condition! { general_io_error: ~RtStream -> ~RtStream; }

  ...

  let p = Path::new(...);
  ...

  do file_no_found_error.cond.raise_default(copy p) {
      let f = File::null_file();
      do general_file_error.cond.raise_default(p, f) {
          File::stream_file(general_io_error.cond.raise(f.rts));
      }
  }

If you moved this kind of idiom into a helper function, you could call it from multiple locations without much boilerplate or possibility of getting the chaining wrong. But it'd be nicer to do it at condition-declaration site, certainly. More arguments for revisiting the condition! macro.

Unfortunately there is no equivalent to `Typeable` yet in Rust, but we
have talked about it (called `Any`). I would also like `fail!()` take an
`Any`, so this seems like an appropriate place for it.

+1 yes please. I suspect we're _close_ to having enough machinery for this now.

-Graydon
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to