Thanks. Then the 'bad' code should look something like: // bad
NotifyIcon CreateNotifyIcon()
{
NotifyIcon icon = new NotifyIcon();
icon.Text = "Blah blah blah";
system.Display(icon);
icon.Icon = new Icon(GetType(), "cool.ico");
return icon;
}
Which would make noticing the coding error quite a bit easier, as we are passing an obviously incompletely initialized object to a third party. As presented, the code presumably is intended to be used like so: NotifyIcon icon = CreateNotifyIcon();
system.Display(icon);
which works in both versions. If 'icon.Icon = new Icon(GetType(), "cool.ico");' throws, then the NotifyIcon is never displayed.As presented, this is hardly an argument that explicit error passing code is better at detecting bad code than exception based code. His other article, https://devblogs.microsoft.com/oldnewthing/20040422-00/?p=39..., is making the point a bit more clearly. The point itself is half-sound. While it is true that with exception based code, it is hard to distinguish which statements may fail, the real issue is about correctly releasing resources. Assume that every statement can fail, and use some form RAII to manage resource cleanup. The exact content of RAII is hard to glean in both explicit error code and exception based code, as it depends on how the specific third party API acquires/releases resources. Though APIs can be organized to essentially force the use of RAII, even if popular but ancient APIs like POSIX file system are not designed that way. The real language design issue is that Java/C# mechanism for running cleanup code 'try {} finally {}' a. fails to pass through a handle to the resource that needs cleanup and b. is not scoping the lifetime of the resource that needs cleanup. Furthermore, the standard API makes no effort to provide RAII constructs for correctly managing resource lifetimes. Those language ecosystems actively steer people towards writing bad code. Exceptions may be fine, but the lack of language supported RAII is definitely poor language design. For better language design, exception based Python offers 'with' mechanism and error code based Golang offers 'defer'. https://www.python.org/dev/peps/pep-0343 https://golang.org/ref/spec#Defer_statements |
I think that's part of Chen's point: programmers just aren't good at doing this, they even get it wrong in published code samples. Exception-handling tends to become an afterthought, and even if you do pay attention to it, it's hard to get right.
Chen is hardly alone in his scepticism. Exceptions are forbidden in the Google C++ style guide. They're also forbidden in certain critical-systems subsets of languages. Ravenscar Ada forbids exceptions, [0] as does Spark Ada (though in that case it's for a slightly different reason: it's difficult to formally reason about exceptions). edit Apparently the JSF C++ standard forbids exceptions, but MISRA C++ permits them provided certain guidelines are followed.
I agree that RAII is very useful for robust exception-handling.
> APIs can be organized to essentially force the use of RAII, even if popular but ancient APIs like POSIX file system are not designed that way
Right, this is just the kind of thing C++ wrappers add (when wrapping C APIs).
> Java/C# mechanism for running cleanup code 'try {} finally {}' a. fails to pass through a handle to the resource that needs cleanup
Short of proper RAII (destructors), I'm not sure what that would look like.
I'm not sure what Chen makes of destructors. They're non-local flow-control, but he seems to like them.
Somewhat related: Zig's optional types, which essentially force the programmer to explicitly handle the case where the data doesn't exist. [1] Much more robust than the approach C takes, where the programmer is trusted to perform the check when necessary.
[0] p20 of PDF: https://www.sigada.org/ada_letters/jun2004/ravenscar_article...
[1] https://ziglang.org/documentation/master/#Optionals