6 ms·
I'd make the argument that Rust is making many of the same mistakes as C++. It's got a kitchen-sink approach that has already accumulated some barnacles, and th
by tdees40 12y ago
I'd make the argument that Rust is making many of the same mistakes as C++. It's got a kitchen-sink approach that has already accumulated some barnacles, and that ownership system is a mess.
- Animats 12y agoThe ownership system, the big innovation in Rust, isn't bad. The type system, macro system, and error handling interact to form a big mess. Exceptions would have been simpler than the mechanisms used to avoid exceptions.
- Animats 12y agoA note on exceptions. Exceptions have a bad reputation in some quarters. They're not in Go, Rust, or C, and they're troublesome in Java and C++. They're successful in Python. Why is this? It took several go-rounds to make Python exceptions generally useful. Originally, exceptions were just strings. Exceptions became more useful when an standard hierarchy of exceptions was defined and libraries were modified to use it. The Python exception hierarchy has a root of Exception, and one of its subclasses is EnvironmentError. Errors that reflect problems external to the program, such as I/O, network, and external data format errors, are subclasses of EnvironmentError. So if you catch EnvironmentError and report it properly, that covers most common cases. Exceptions not under EnvironmentError generally indicate program bugs, so those are usually allowed to propagate upward to the point where crash-level errors are handled. The default is a program abort and a stack backtrace. The other problem is getting things closed out properly after an exception. C++ tried doing this by having destructors close things. This works until something goes wrong in a destructor. Exceptions raised within destructors tend to cause problems. This tends to result in ignoring errors at close time. Go's "defer" has the same problem. Calling destructors from a garbage collector is even worse. Microsoft tried to address this in Microsoft Managed C++, resulting in painfully complicated destructor semantics. (Look up "re-animation" for Managed C++, if you like.) The Python solution was a "with" clause. This is borrowed from Common LISP's "(with-open-file ..) construct. The Python syntax is "with opening expression as object handle :". The opened object is open for the scope of the with clause, and upon exit from the with clause, the "__exit__" method of the object will be called, no matter how the with clause is executed. To make this work, I/O, locks, and database connectors had to be retrofitted with "__exit__" methods. This was done around Python 2.5-2.6; it took a while. The result is that Python functions that open and close things tend to have this pattern: def dosomething(args) : try: with *thingtoopen* as *handle1* : with *otherthingtoopen* as *handle2* : *do work on open things* except EnvironmentError as message : *report errors which occurred opening things, working on them, or closing them* This is reasonably simple, and tends to result in programs that do something reasonable for error conditions. Importantly, even if an exception is raised, the close operations take place. Even the case of a close operation raising an exception is properly handled; the other close operations still take place, but they are aware there's an exception situation. A database connector would do a ROLLBACK instead of a COMMIT in such a situation. This is probably the direction Rust should have taken. Error handling in Rust requires excessive boilerplate code every place an error can occur. The new Rust error hierarchy scheme [1] is especially verbose. [1] http://lucumr.pocoo.org/2014/10/16/on-error-handling/ http://lucumr.pocoo.org/2014/10/16/on-error-handling/
- Jweb_Guru 12y agoPython's with has no advantages at all over destructors that I'm aware of (it does have some disadvantages, since it's easy to forget) and I'm curious to ask what you think they are. This may help those of us who are extremely happy with Rust's error handling story better understand your complaints. You keep talking about "excessive boilerplate" and the like, but that simply isn't what I observe in practice--it could certainly be better, but existing solutions are fairly unobtrusive. You also claim that exceptions are "successful" in Python, but I have had just as many issues with exceptions in Python as I have in Java (if not more, because the exceptions are not part of the function signature). The problems with exceptions are well-documented (they require the programmer to write all his or her code transactionally, leading to so-called exception safety issues). Subclassing doesn't address the major issues of error handling at all, as far as I know, but again--I'd be interested to hear how you feel they are different (Rust already has an IOError that suffices for the cases you seem to be concerned about, but in practice there are many more types of error than that).
- Animats 12y agoHacker News isn't really a good place to post code, but here's an excerpt of the boilerplate required in Rust code that reads XML to pass I/O, HTTP, and XML parsing errors upward. This, according to the reference above on the new style of error handling, is now the official right way to do it.[1] Works fine. You get to repeat all this each time you define a new result type with possible lower level errors. What's wrong with this picture? // // Return type and its error handling // pub type FeedResult<T> = Result<T, FeedError>; /// A set of errors that can occur handling RSS or Atom feeds. #[derive(Debug, PartialEq, Clone)] // crank out default functions pub enum FeedError { /// Error detected at the I/O level FeedIoError(IoError), /// Error detected at the HTTP level FeedHTTPError(hyper::HttpError), /// Error detected at the XML parsing level FeedXMLParseError(xml::BuilderError), /// XML, but feed type not recognized, FeedUnknownFeedTypeError, /// Required feed field missing or invalid FeedFieldError(String), /// Got an HTML page instead of an XML page FeedWasHTMLError(String) } // // Encapsulate errors from each of the lower level error types // impl error::FromError<hyper::HttpError> for FeedError { fn from_error(err: hyper::HttpError) -> FeedError { FeedError::FeedHTTPError(err) } } impl error::FromError<old_io::IoError> for FeedError { fn from_error(err: IoError) -> FeedError { FeedError::FeedIoError(err) } } impl error::FromError<xml::BuilderError> for FeedError { fn from_error(err: xml::BuilderError) -> FeedError { FeedError::FeedXMLParseError(err) } } impl fmt::Display for FeedError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { &FeedError::FeedIoError(ref xerr) => xerr.fmt(f), // I/O error &FeedError::FeedHTTPError(ref xerr) => xerr.fmt(f), // HTTP error &FeedError::FeedXMLParseError(ref xerr) => xerr.fmt(f), // XML parse error &FeedError::FeedUnknownFeedTypeError => write!(f, "Unknown feed type."), &FeedError::FeedFieldError(ref s) => write!(f, "Required field \"{}\" missing from RSS/Atom feed.", s), &FeedError::FeedWasHTMLError(ref s) => write!(f, "Expected an RSS/ATOM feed but received a web page \"{}\".", s) //_(ref xerr) => xerr.fmt(f) // would be convenient, but not allowed in Rust. } } } [1] http://lucumr.pocoo.org/2014/11/6/error-handling-in-rust/ http://lucumr.pocoo.org/2014/11/6/error-handling-in-rust/
- kibwen 12y agoIf you think that Rust is a kitchen sink language or that the ownership system is a mess, then I have to assume that you have neither followed the language through its development nor used it. It catches flak every day for not being a kitchen sink language, and the ownership system is probably the only part of it that's so finely-polished that I have no faults with it. Now, Rust macros? Those are gross. The module system is love-it-or-hate-it. The syntax could be most charitably described as "classical" and the compiler itself is held together with reams of duct tape. But it's the first language that's attempting to explore this design space in an industrial setting and encounter all the problems therein (for god's sake, they had to reinvent the notion of closures (and even if you superficially believe they're similar to C++11 closures, the mechanisms required to make C++-style closures memory-safe are novel AFAICT)). Despite all of its flaws (and the upcoming 1.0-stable release will have flaws), it's a remarkably worthwhile language to learn and will influence every language to come that cares to call itself a systems programming language, all because the core ownership system has been so long iterated-upon (and even that will continue to see ergonomic improvements as time goes on).
- IshKebab 12y agoThe kitchen sink approach is why C++ is still so popular. I'm glad Rust is multi-paradigm.