I need to learn Rust again. I haven't used it in a year, and the language has changed a lot. Everything now seems to require a closure or some ".into()" idiom. Like this:
Type Result has its very own set of control flow primitives.[1] So does type Option.[2] Any type can have its very own flow control primitives. Are we going to see
date.if_weekday(|day| { ... })
and similar cruft for every type? I hope not.
Hopefully the setup of closures that probably won't be executed isnt't too expensive. Do those require a heap allocation and release when not used, or is this all on the stack? The run time variation for small changes indicates that some constructs are more expensive than others, but it's hard to know which ones are bad.
I understand the rationale behind the Rust approach to error handling, but it's just painful to look at. After seeing the gyrations people go through in Go and Rust to deal with the lack of exceptions, it looks like leaving exceptions does not make a language simpler. From a compiler perspective, the compiler can generally assume that exceptions are the rare case, and can optimize accordingly. Rust has no idea which closure a function will call, if any.
> Hopefully the setup of closures that probably won't be executed isnt't too expensive. Do those require a heap allocation and release when not used, or is this all on the stack?
Creating a closure with |...| { ... } is literally as expensive as creating a tuple containing (references to) the variables it captures. That is, they're on the stack by default like everything else in Rust, and there's no implicit heap allocations. For more details, see, for instance, http://huonw.github.io/blog/2015/05/finding-closure-in-rust/ .
> Rust has no idea which closure a function will call, if any.
It does. As my link above discusses, each closure has a unique type, allowing monomorphisation to kick in and hence the compiler can easily optimise and inline calls to closures (as long as the author of the closure-taking function doesn't opt-in to only allowing virtual dispatch).
Closures are just as expensive as normal control flow, if they're not passed as a trait object (which is unusual).
To expand: No closures require heap allocation. If you pass the closure as trait object, calling it will require a virtual function call, which is more expensive than a normal one.
Closures aren't more expensive than what you use; they're not heap-allocated by default, and a closure that doesn't close over everything should end up being a regular old function.
We just merged an RFC for ? syntax, which should make error handling less verbose.
> it looks like leaving exceptions does not make a language simpler.
I don't think that this ever was the claim. Rust leaves exceptions out because they make code hard to work with and because it prefers errors be acknowledged. I don't think monadic errors are "simpler" than exceptions.
(Though I suspect they are simpler to teach than exceptions to people who have learned neither)
Once `?` lands in Rust I expect these "gyrations" become much simpler.
> Are we going to see
>
> date.if_weekday(|day| { ... })
>
> and similar cruft for every type? I hope not.
Not sure where this comes from. As the others have mentioned, closures are pretty cheap, but anyway you don't need to use the control flow primitives. There are many ways of dealing with errors, including using `if let`, `match`, `unwrap_or_else`, `try!`, and the upcoming `?`.
Hopefully the setup of closures that probably won't be executed isnt't too expensive. Do those require a heap allocation and release when not used, or is this all on the stack? The run time variation for small changes indicates that some constructs are more expensive than others, but it's hard to know which ones are bad.
I understand the rationale behind the Rust approach to error handling, but it's just painful to look at. After seeing the gyrations people go through in Go and Rust to deal with the lack of exceptions, it looks like leaving exceptions does not make a language simpler. From a compiler perspective, the compiler can generally assume that exceptions are the rare case, and can optimize accordingly. Rust has no idea which closure a function will call, if any.
[1] https://doc.rust-lang.org/std/result/enum.Result.html [2] https://doc.rust-lang.org/std/option/enum.Option.html