| I don't have an account there so I'll comment here: > In particular, allocating a new object and returning a reference to it it from a function is common in C++ but difficult in Rust, because the function doing the allocation doesn't know the expected lifetime of what it returns. This is what boxes are for. A Box is a unique pointer to a value on the heap and can be used without knowing compile-time lifetimes. References and lifetimes allow you to safely return pointers to stack allocated objects. In C++, you'd have to do this: MyType value;
my_function(&value);
When returning references, rust uses the lifetimes instead of explicit declarations to figure out where (on the stack) `value` needs to be allocated.> Declarations are comparable in wordiness to C++. Only at interfaces where the declaration also serves as documentation. Elsewhere, types can generally be inferred. > Rust has very powerful compile-time programming; there's a regular expression compiler that runs at compile time. I'm concerned that Rust is starting out at the cruft level it took C++ 20 years to achieve. I shudder to think of what things will be like once the Boost crowd discovers Rust. Unlike C++,
1. Macros from one crate aren't imported into another unless the user explicitly requests that they be.
2. Macro invocations are clearly macro invocations. You never have to wonder if something is a function or a macro. > The lack of exception handing in Rust forces program design into a form where many functions return "Result" or "Some", which are generic enumeration/variant record types. These must be instantiated with the actual return type. As a result, a rather high percentage of functions in Rust seem to involve generics. How is this a problem? > There are some rather tortured functional programming forms used to handle errors, such as ".and_then(lambda)". Doing N things in succession, each of which can generate an error, is either verbose (match statement) or obscure ("and_then()"). You get to pick. Or you can just use ".unwrap()", which extracts the value from a Some form and makes a failure fatal. I agree that this is less than ideal. However, IMHO, this is better than Java and C++. Java: Libraries tend to bubble everything. This leads to long throws clauses in function signatures with unexpected exceptions. A user of these libraries often catches and ignores these exceptions when writing the first draft of his or her programs because they don't make sense (why handle IO Errors when using a collection?). And then, because his or her program works, he or she forget about the ignored exception cases turning them into silent errors. On the other hand, in rust, you can only return one error. When writing a function that has multiple failure modes, this forces the programmer to think about the set of failures that can happen and come up with new error type. This doesn't force the programmer to come up with a meaningful error type but it gives them the opportunity. Additionally, like in Java, Rust programmers can ignore errors (`unwrap()`). However, unlike in Java, these ignored errors are not silent, they are fatal. C++: Exceptions are unchecked and everyone I've talked to avoids them like the plague. In the end, C++ exceptions end up acting like rust's `panic!()` because programmers don't check them but are used like Java's exceptions because programmers could check them. > There's a macro called "try!(e)", which, if e returns a None value, returns from the enclosing function via a return you can't see in the source code. Such hidden returns are troubling. I agree that hidden returns can be troubling. However, in rust, only macros can lead to hidden returns, macros use a special syntax (`macro_name!(args...)`, and macros have to be explicitly imported. > All lambdas are closures (this may change), and closures are not plain functions. They can only be passed to functions which accept suitable generic parameters. This is because the closure lifetime has to be decided at compile time. The first sentence is correct but the last two are just wrong: fn takes_a_function(f: Box<Fn()>) {
(f)();
}
fn main() {
takes_a_function(Box::new(move || { println!("hello world") }));
}
The `Box` allocates the closure on the heap and the `move` causes the closure to capture by value. This means that this closure (`f`) can be moved freely without lifetime restrictions because it doesn't reference the stack. However, most functions that accept closures use generics and do any necessary boxing internally to make the user's life easier.> Rust has to do a lot of things in somewhat painful ways because the underlying memory model is quite simple. This is one of those things which will confuse programmers coming from garbage-collected languages. Rust will catch their errors, and the compiler diagnostics are quite good. Rust may exceed the pain threshold of some programmers, though. Rust is a systems language. It exposes a lower-level (not simple) memory model because systems programmers need it. If you want garbage collection, you are free to roll your own (yes, you can actually do this in rust). > Despite the claims in the Rust pre-alpha announcement of language definition stability, the language changes enough every week or so to break existing programs. Re-read those claims. Alpha means fewer breaking changes and no "major" breaking changes not stability. |
OMG, thank you for including this. I spent several months reading every bit of documentation that was available for Rust, and programming in it daily. Made some good progress. But I never, never came across this explanation. Very enlightening.
Rust desperately needs documentation covering these kinds of details. How on earth is someone supposed to make serious use of the language without knowing this?
I believe the Klabnik documentation hinted at this (something like, "The Rust compiler is smarter than that" and therefore you don't need to overuse pointers), but by no means did it actually spell it out. And you only needed a few sentences to cover it.
I know the Rust community is aware that more documentation is needed and has a todo list a mile long. But I don't know if technical details such as this are high enough on the priority list.