6 ms·
It took 20 years to get OCaml where it is. You can always wait another 20 years, and only start using Rust then. Otherwise, it seems to me that what you are as
by fluffything 7y ago
It took 20 years to get OCaml where it is. You can always wait another 20 years, and only start using Rust then.
Otherwise, it seems to me that what you are asking is "Why didn't the Rust developers ship a 20 year old language on day 1?".
- lmm 7y agoThe way I see it, Rust's day-1 error handling story was significantly worse than that of most recent languages (e.g. Swift, Typescript, Kotlin, Dart, just going down Wikipedia's list of significant languages). And it's not clear what Rust gains from that - after a lot of a churn it's not come up with a significantly better error-handling paradigm than what was already out there. So it just feels like a waste.
- roca 7y agoWhat was worse about Rust 1.0's error handling that, say, Typescript's Result? They look very similar to me. What do you mean by "a lot of churn"? The only significant thing I can think of is that the try! macro turned into the ? operator.
- lmm 7y agoComposing first-class functions is much more idiomatic in typescript, because there's only one function type, so you can do the railway-oriented programming style. In a sense Rust doesn't quite have first-class functions yet (and I'd argue that NLL actually made functions less first-class than they were before): you can't always pull out a given block of code into a closure, because the resulting code may not pass the borrow checker. Whereas in Typescript (() => x)(), (a => x)(a) etc. are always equivalent to x.
- roca 7y agoThat's true but that seems to be an inevitable consequence of the Rust ownership model. Which means this is just another example of where the "one big innovation" in Rust just isn't orthogonal to other language design issues, as you suggested it should be.
- lmm 7y agoTo a certain extent yes, but it's a problem that they've made much harder for themselves by trying to support both OCaml-style first-class functions and C/Java-style imperative control flow. If the NLL work around control-flow keywords had gone into first-class functions, I suspect we'd see a good solution by now; conversely if they'd committed to C++/Java style with what works there (i.e. exceptions) that would also probably make for a result that was easier to use.
- fluffything 7y agoRust 1.0 error story hasn't changed much? They added `expr?` as sugar for `try!(expr)`, but that's about it.
- majewsky 7y agoThe big problem is that there isn't a single error type that every function can return. Take this example: fn read_i32_from_file(path: &str) -> Result<i32, ???> { let bytes = std::fs::read(path)?; //may return std::io::Error let text = std::str::from_utf8(&bytes)?; //may return std::str::Utf8Error return text.parse(); //may return std::num::ParseIntError } What do you enter as error type in the type signature? In most cases, it would be sufficient to use Box<dyn Error>, but that requires boxing and thus an allocation, which is something that many libraries want to avoid to be useful for no_std or resource-constrained usecases. An answer to this could be to define enum ReadI32FromFileError { Read(std::io::Error), Decode(std::str::Utf8Error), Parse(std::num::ParseIntError), } and then you have to provide a bunch of trait implementations for that new error type (esp. to cast the constituent error types into this type). That's a whole lot of boilerplate, and most of the error handling crates are about cutting down on this boilerplate. Another problem with the std::error::Error trait is that until 1.30 it didn't provide a nice way to traverse error chains. Even now, the new cause() method may not be supported by all the error types from all the libraries you care about.
- richardwhiuk 7y ago`Box<dyn Error + Send + Sync>` is almost always fine
- ragnese 7y ago> What do you enter as error type in the type signature? In most cases, it would be sufficient to use Box<dyn Error>, but that requires boxing and thus an allocation, which is something that many libraries want to avoid to be useful for no_std or resource-constrained usecases. Okay, so you are in a resource-constrained environment. What would you do in C or C++? You can't do exceptions in C++ here, because there's allocation and other ugliness for embedded or whatever. You can always have your rust code return an int, like we do in C if the boilerplate is too much for you. On the other end, you have Kotlin and Swift Results. Kotlin type-erase the error completely. And I'm pretty sure Kotlin's Result allocates on failure. I'm not sure about Swift. But are you running Swift, with its heap allocated `class` and reference counting on embedded? You're surely not running Kotlin.
- skue 7y ago> Rust's day-1 error handling story was significantly worse than that of most recent languages (e.g. Swift, Typescript, Kotlin, Dart, just going down Wikipedia's list of significant languages). And it's not clear what Rust gains from that... Not sure I understand... All these other recent languages you mentioned were released after Rust, so Rust couldn’t have benefited from them on day 1. The opposite happened: most of these other languages took design lessons from the languages that preceded them, including Rust.
- lmm 7y agoI'd say the same thing for the equivalent list of pre-Rust languages (with the exception of Go): Clojure, F#, Scala, D, C#.