Concept proposal: Safely catching error
Olivier FAURE via Digitalmars-d
digitalmars-d at puremagic.com
Mon Jun 5 02:50:15 PDT 2017
I recently skimmed the "Bad array indexing is considered deadly"
thread, which discusses the "array OOB throws Error, which throws
the whole program away" problem.
The gist of the debate is:
- Array OOB is a programming problem; it means an invariant is
broken, which means the code surrounding it probably makes
invalid assumptions and shouldn't be trusted.
- Also, it can be caused by memory corruption.
- But then again, anything can be cause by memory corruption, so
it's kind of an odd thing to worry about. We should worry about
not causing it, not making memory corrupted programs safe, since
it's extremely rare and there's not much we can do about it
anyway.
- But memory corruption is super bad, if a proved error *might*
be caused by memory corruption then we must absolutely throw the
potentially corrupted data away without using it.
- Besides, even without memory corruption, the same argument
applies to broken invariants; if we have data that breaks
invariants, we need to throw it away, and use it as little as
possible.
- But sometimes we have very big applications with lots of data
and lots of code. If my server deals with dozens of clients or
more, I don't want to brutally disconnect them all because I need
to throw away one user's data.
- This could be achieved with processes. Then again, using
processes often isn't practical for performance or architecture
reasons.
My proposal for solving these problems would be to explicitly
allow to catch Errors in @safe code IF the try block from which
the Error is caught is perfectly pure.
In other words, @safe functions would be allowed to catch Error
after try blocks if the block only mutates data declared inside
of it; the code would look like:
import vibe.d;
// ...
string handleRequestOrError(in HTTPServerRequest req) @safe {
ServerData myData = createData();
try {
// both doSomethingWithData and mutateMyData are @pure
doSomethingWithData(req, myData);
mutateMyData(myData);
return myData.toString;
}
catch (Error) {
throw new SomeException("Oh no, a system error
occured");
}
}
void handleRequest(HTTPServerRequest req,
HTTPServerResponse res) @safe
{
try {
res.writeBody(handleRequestOrError(req),
"text/plain");
}
catch (SomeException) {
// Handle exception
}
}
The point is, this is safe even when doSomethingWithData breaks
an invariant or mutateMyData corrupts myData, because the
compiler guarantees that the only data affected WILL be thrown
away or otherwise unaccessible by the time catch(Error) is
reached.
This would allow to design applications that can fail gracefully
when dealing with multiple independent clients or tasks, even
when one of the tasks has to thrown away because of a programmer
error.
What do you think? Does the idea have merit? Should I make it
into a DIP?
More information about the Digitalmars-d
mailing list