4 ms·
> - The type system is unusual, and complex. It's hard to do anything without templates. You need generics to have the borrow checker that you praise above. Ot
by pcwalton 10y ago
> - The type system is unusual, and complex. It's hard to do anything without templates.
You need generics to have the borrow checker that you praise above. Otherwise references would be basically crippled.
> The template libraries are heavily biased towards closure-oriented functional programming.
No, they aren't. I use for loops all the time in Rust, and it's totally natural to do so. My general guideline in my own code (which is followed in projects like WebRender and Servo layout) is that if it's one line, I use the functional idiom; if it's more than one line, I use a for loop.
> Parts of expressions are nameless and have no visible type. This is terse but hard to maintain.
Are you thinking of unboxed closures here? If so, they're necessary to avoid unnecessary heap allocations in normal code. If we didn't have that, then we couldn't compete with C++11 closures.
> - The hacks needed to avoid the need for exceptions are uglier than exceptions.
Unwinding is the single most divisive issue in Rust. We would alienate most of our community (I'm not kidding) if libraries required it. For a time, there was a language fork over the issue until it was resolved.
> Unlike the borrow checker, none of these things are clearly improvements. They're just different.
No, they're needed for the borrow checker and most of the other features to work.
> I would have gone for Python-type exceptions rather than error enums and macros
Then, as above, you instantly alienate all embedded developers.
> single-inheritance OOP rather than traits
This doesn't work in practice unless you introduce interfaces: it's way too inexpressive. This is why Java and C# have interfaces. Traits are just interfaces.
> Python-type with clauses rather than RAII.
You want to force an extra layer of indentation for every Box or Vec you create, and leak memory if you forget to write "with"? No thank you!
- Animats 10y agoYou want to force an extra layer of indentation for every Box or Vec you create, and leak memory if you forget to write "with"? No thank you! No, no, Python like with clauses, for opening files and such.
- dbaupp 10y agoRAII (i.e. destructors that automatically run on values when leaving a scope) is what lets one avoid manually freeing Box and Vec, so you want to split the world into "destructor resources" (RAII) and "with resources", maybe with some guidelines for when to choose which. Presumably a data type that contains a "with resource" would itself have to be a "with resource" (or else why bother?). This makes "with"-ness abstraction-piercing and infectious, so, for backwards-compatibility, if there was any chance of a type eventually containing a "with resource", the programmer would have to mark the type as such a resource preemptively, or else be forced to make a breaking change to their library. All for a minor syntactic difference in how one constructs values. Also, how does one return values from `with` clauses? It's useful to be able to write a function that returns a file, so one would need yet more machinery, just to be able to cancel the normal with-cleanup. Again, all for a relatively minor syntactic distinction.
- pjmlp 10y agoNot necessarily. On languages that support currying, or leaving the last parameter out of the call if it is a lambda, you can create high-order-functions for resource management. The effort is no different than implementing RAII classes. withDBConnection { r = db.execute ("SELECT....") } Here the function withDBConnection calls the lambda, providing it an implicit parameter for the db connection, which it fully controls. There are more fancy ways to improve on this, specially if the language also allows for macros. So I find a bit sad, that the discussions of with/using/try don't focus on the FP way of doing resource management.
- dbaupp 10y agoThat has both of the problems I mentioned even if it isn't a full language level construct.
- pjmlp 10y agoI fail to see how. The resources are only available via the lambda parameters, so the wrapper function controls the lifetime, arena style. Last expression in the block is the return value.
- dbaupp 10y agoThe wrapper function controlling the lifetime is exactly why that scheme has both of the problems I mentioned. It all comes down to: how do you write a fn connection() -> DB? You can't (or, at least, it doesn't make sense to) return the parameter from withDBConnection: the whole point is the wrapper function does the clean-up when it is done, invalidating the connection object. The only way to pass the value along is to have `connection` also take a closure, i.e. manual continuation passing style/node.js's "closure-hell". This can, in some cases, be addressed with async/await style sugar, but there's still semantic (mainly around composition) and performance issues with it. Anyways, going back to the data type: if one has a type Foo with a constructor like fn create() -> Foo, and Foo is updated to contain a database connection, that constructor no longer works, and needs to also be converted to use CPS. That is, the interface of the Foo object has be infected by "private" details, meaning needs to to preemptively decide whether there is any chance of each datatype needing to hold one of these CPS objects. (Of course, the code you suggest works fine in Rust now, and is even used for certain special cases, but that is very different to baking it into the language as a whole new syntactic construct.)
- Manishearth 10y agoHis comment still stands. RAII is used in Rust for a lot of things, the least of which being managing files. RAII works as an interpoerable, implicit solution for both (unlike Python, where a lot of the use cases get magicked by the GC). with needs to be explicit (or else you leak), and is just more cumbersome to use, given how many things depend on it in Rust. In python you only use with for a few types, so the verbosity (and the fd leaks if you forget it) are no big deal. Rust has no GC, and it uses it for a lot more.
- pcwalton 10y agoPython uses destructors/finalizers to free OS resources and a GC to free memory. Because Rust has no GC, it uses destructors for memory and OS resources. So adopting Python's scheme for calling destructors would force us into using that for memory as well.
- oconnor663 10y ago>> The template libraries are heavily biased towards closure-oriented functional programming. > No, they aren't. I think that came off more combative than you meant it to :) >> Parts of expressions are nameless and have no visible type. >Are you thinking of unboxed closures here? I think Animats is complaining about the functional style in general, which is at least more common in Rust than in C++ or Python. And if that's right, it's a complaint I mostly agree with. Functional programming can cram quite a bit into one line, and that's all well and good, but a lot of the savings comes from omitting the variable names that are usually there in imperative code, and some clarity gets lost when you don't name things. I agree with your rule of thumb. Functional stuff is great when what it's doing is obvious.
- pcwalton 10y ago> a lot of the savings comes from omitting the variable names that are usually there in imperative code, and some clarity gets lost when you don't name things. Are you talking about point-free style? Point-free style is rare in Rust, partially by convention, partially due to the fact that references and values are different and often you need to wrap in a closure to convert one to the other.