I've been using Rust to process a large text corpus, and as Armin suggests, it's a really interesting experience. Here are a few things I've noticed so far:
1. Writing Rust code definitely takes more time than Python or Ruby, but it's not bad in practice. I can't measure the productivity difference yet, partly because I'm still learning Rust. I do spend more time thinking about how to write zero-allocation and zero-copy algorithms, and trying to get my code to compile (but see 3, below).
2. It's possible to write very fast code without compromising memory safety. It's great to see key parsing operations happening in ~100 nanoseconds, with no allocations.
3. Rust is one of those languages where I need to work to make the compiler happy, but once I manage that, the code generally works on the first try.
4. The tooling is very good: library management, unit testing, benchmarking, documentation, error messages etc., all work very nicely.
Overall, it lacks the instant gratification of a really good scripting language. But I keep finding myself back in Emacs, writing more Rust code for the fun of it. And I really enjoy the combination of low-level code, memory safety, and a useful selection of functional programming features. I'm glad to see a new language in this niche.
> Rust is one of those languages where I need to work to make the compiler happy, but once I manage that, the code generally works on the first try.
I can confirm this. I have a CSV parser[1] that is maybe twice as fast as Python's CSV parser (which is written in C)+. There's nothing magical going on: with Rust, I can expose a safe iterator over fields in a record without allocating.
All things a proper understanding of regex and a minimal understanding of streaming file IO can cover. The whole "if you think regex is the solution to your problem, now you have two problems" thing has gotten out of hand. Regex is not that hard.
It's simpler to handle quotes and backslashes escaping commas with a custom parser. And then there's the domain knowledge baked into the lib. Does your regex solution produce excel compatible csv files when you have leading zeros? That's important to some people.
I think people who are downvoting me don't understand how ludicrously retarded most people who output CSV are. CSV is not RFC4180. It's whatever bullshit text file your client has handed you and convinced your project manager is your problem to parse, not their problem to generate even remotely correctly. There is no CSV library capable of handling "CSV". Every time someone asks you for it, you better kick and scream or expect to do a custom job.
I think people are being a fit unfair downvoting you right now (I bumped you up) - but I also disagree with you.
When I'm working with a file that purports to be CSV/TSV, with python, I reach out to the CSV module, specify the dialect that created it - and instantly get all the power of being able to identify refer to all the fields, rows without having to otherwise worry about parsing them.
Is it 100% bulletproof - definitely not - but, then again, I'm not writing a life safety system. And I've also never had the Python CSV parser break on any reasonable file I've sent it.
I'm truly thankful for robust CSV/TSV parsers. Throwaway code like this just works - in particular handles parsing the column headers to automatically build the dict for me.
sitesFN=['gateway.tsv','relay.tsv']
dsites={}
for fn in sitesFN:
f=open(fn,'r')
reader=csv.DictReader(f,dialect='excel-tab')
for row in reader:
dsites[row['NIC_Serial_No']]=row['Device_Name']
Perhaps what you are trying to say that people are failing to hear, is that you can't rely on a CSV parser to, a priori, handle all possible files that purport to be "TSV/CSV" - in that I agree with you, and that you will always need to examine the file and determine if the built in parser will handle it.
But - What if it turns out the standard library CSV parser handles the "CSV" file just fine - in that case it seems to make a lot of sense to use it, rather than taking the time in writing your own (along with the bugs that come from re-writing anything).
And, speaking just for myself - again, I've never seen a CSV/TSV file that the python didn't handle just fine - not to say they aren't out there - you just have to go out of your way to create them.
> And, speaking just for myself - again, I've never seen a CSV/TSV file that the python didn't handle just fine - not to say they aren't out there - you just have to go out of your way to create them.
Indeed. Python's CSV module supports a "strict" mode that will yell at you more often, but by default, it is disabled. When disabled, the parser will greatly prefer a parse over a correct parse. I took the same route with my CSV parser in Rust (with the intention of adding a strict mode later), because that's by far the most useful implementation. There's nothing more annoying then trying to slurp in a CSV file from somewhere and having your CSV library choke on it.
The csv library of python has handled every csv i've ever thrown on it, csv is "standardized" enough for that. Just set two things, the delimiter, the escape quote method and be done with it. The output is a list of dictionaries with the column headers as keys, very elegant. The best part is that using the same input you did to read a file you can use to save/modify the file and be sure it will look the same when your client re-opens it. A regexp would take twice the time to write and wouldn't give you half of those features, and it would probably fail at escaping sooner or later, for the same reason as xml can't be parsed with regexps.
Well, if you look at, eg, Python's CSV parsing library, it's been more than enough to cover my needs so far, and handles different CSV flavours. It is much nicer and less error-prone to use than using regexps.
You are getting downvoted to hell but I can see what you're meaning. Generally if someone hands you a CSV file there is no guarantee that something mental isn't happening as there is no "CSV" standard. So you're saying that when your task is "process the client's CSV file" you might not necessarily be able to rely on a library handling it correctly, and that you should prepare to get your hands dirty (perhaps hacking together something with a regex or two).
He actually has a point there. There are so many different versions of "CSV" floating around that I'm not at all sure I'd want to deal with a parser that could handle most of them. Ever generated a CSV file from a spreadsheet or DB interface program? Did it have a big list of options on how the CSV would be formatted, so you could easily read the generated file into whatever downstream you were using?
> I'm not at all sure I'd want to deal with a parser that could handle most of them.
Python's CSV parser will handle almost anything you throw at it and it is widely used to great success.
> Ever generated a CSV file from a spreadsheet or DB interface program? Did it have a big list of options on how the CSV would be formatted, so you could easily read the generated file into whatever downstream you were using?
Just about every single CSV file that I've ever had to read was generated by someone other than me. Frequently (but not always), they come from a non-technical person.
Sometimes those CSV files even have NUL bytes in them. Yeah. Really. I swear. It's awful and Python's CSV parser fell over when trying to read them. (You can bet that my parser won't.)
> He actually has a point there.
His point is to use regexes instead of a proper CSV parser. I'm hard pressed to think of a reason to ever do such a thing:
1. A regex is much harder to get correct than using a standard CSV parser.
2. A regex will probably be slower than a fast CSV parser.
A regexp would work, but it's usually the wrong level of abstraction to operate at. One wants to say "for the next 2,000 rows, retrieve columns 2 and 4, and the column labeled 'foo'", not write a regexp.
This is great feedback, thanks. And if there's anything that you don't like about Rust, please let us know now while we still have a chance to possibly fix it! Only a few short months left until all of our mistakes are forever entombed in Rust 1.0. :)
I've been using Rust for toy projects for a while, and I gotta say Rust is one of the nicest languages out there. Kudos to the Rust team.
As a relative newbie to Rust, one of the biggest hurdles I faced was that of poor documentation. A lot of "rust xxx" searches would link to outdated articles or to stale links in the Rust official docs. I understand that Rust is a young language, and documentation is probably the last thing on the core devs' to-do list (and rightly so), but I think it would greatly help drive adoption rates if we could proper docs in place.
It gets a bit annoying that a lot of Rust libraries are named something rust-themed. I was looking at which web framework I should use (between Nickel and Iron) and so searching "rust nickel vs iron" returned absolutely nothing of relevance.
I did learn something about metals, though, so there's that.
Hah yes that is definitely an annoying property of "cutesy" library names, they're cute and fairly easy to remember but finding them afterwards is a pain in the ass compared to unfun dreary names like "rust-web".
I think that mutability should also be recorded in types (i.e. even a &mut would not allow you to modify an immutable type). Then, programmers could control whether variants/enums are "fat" (use the maximum amount of memory of any variant in the same type) or "thin" (use the minimum amount of memory for the given variant) by changing the mutability of fields they are stored in.
But I totally understand that you probably have other, well thought-out reasons not to do that.
Off the top of my head this seems like it would allow the amount of memory taken up by an enum to vary dynamically, which would prohibit stack allocation and require heap allocation instead. It would also be impossible to realize this optimization for any array of enums, since they would all need to be the same size for efficient indexing, and further it would wreak havoc with structs containing enums by making it impossible to statically compute field offsets.
However, we have considered elevating the position of mutability in the type system for other reasons--specifically, to make it possible to write a function that is generic with respect to mutability. But there's never been a huge amount of enthusiasm for that, so I wouldn't expect any changes of that sort.
But thanks for the feedback anyway! :) It's definitely valuable to know that people do care about the size of enums. We have various optimizations required by the language spec to shrink them down wherever we can do so while preserving semantics, and we welcome people to concoct more.
Well, it would definitely need to be special-cased, i.e. only use it when (1) the field containing the enum is immutable, and (2) it's the last field in the structure (it would then make the structure a Dynamically Sized Type).
I only care about the size of enums inasmuch as I find it wasteful to allocate a 10 words long Box to hold a 2 words long enum. Of course, the optimization would only make sense if the Box was immutable (even with a unique reference). I mostly mentioned the enum size issue because I read somewhere not too long ago that Servo people were having problems with memory usage because of that (and that they also want objects/records with inheritance of fields - that would bring all the issues you speak about above).
It's true, enum design in Rust can be a fine art. Specifically, knowing when to make the tradeoff between big inlined enum variants and pointer-sized variants that require an indirection to access. This plagued the Rust compiler for a long time as well, as the AST was formerly represented by a ludicrously large enum with something like 120 words per node.
Two things that come to my mind: 1) General concurrency/parallelism ease of use. It would be nice to have Async/Await a la C#, as well as some easier to use channels and synchronization primitives/functions along the lines of Go or Core.Async. And 2) use of <> for type annotations instead of [], which is much easier to parse when nested.
Servo is definitely demanding a wide variety of approaches to both concurrency and parallelism, so I expect the facilities for such in Rust to mature very quickly (but faster with more help and feedback!). It's a very important issue that is seeing work from full-time developers as we speak.
As for the second point, we've gone beyond the bikeshed (<> and [] are both exactly as ambiguous to the grammar, so it's just an argument of preference by this point) and addressed the root issue with `where` clauses, which prevent you from needing to nest <> at all for a majority (or perhaps plurality, I haven't measured) of function signatures where you previously needed to.
The ability to evaluate functions at compile time would be really nice. I was sort of looking at writing a macro in Rust to mirror Nimrod/Nim's 'when' statements but gave sort of gave up when I saw that. But of course that's probably an entirely unreasonable thing to request in the remaining time you have and it might be incompatible with having a borrow checker anyways.
The borrow checker looks really valuable though, and something that might have a big influence on language design going forward.
Lightweight compile-time evaluation is definitely a weakness of ours. A full-fledged syntax extension can get you pretty far, but those are a terror to write and ugly to import. While I don't know of any proposals in the air at the moment, I expect the developers will treat this with great importance post-1.0.
Interesting. From what I understand, this was what BurntSushi's regex macro was intended to accomplish: an efficiently compiled regex that optimized away all unneeded functionality for regex literals. With CTFE, would this get rid of the need for a macro entirely?
1. You cannot compile your program with an invalid regex.
2. The regex runs in a specialized VM corresponding to the specific bytecodes for a regex. This results in an across-the-board performance improvement, mostly because there are some places that can now use stack allocation instead of heap allocation. (But it's still a full NFA simulation, so it isn't going to, say, beat RE2/C++ quite yet.)
Basically, if CTFE allows you to write functions that produce AST transforms, then the `regex!` macro should be convertible to that form. Most of the syntax extension is a quasiquoted copy of the general VM: https://github.com/rust-lang/rust/blob/master/src/libregex/v... --- The actual syntax extension bits are pretty minimal.
Full disclaimer: the `regex!` macro does have a couple downsides. #1, it's a syntax extension, so it has a dynamic dependency on `libsyntax`. #2, it ends up producing a lot of code and bloating your binary if you use it a lot (you might start noticing at after the first few dozen, and you might start cursing it after the first few hundred).
Sadly I'm not enough of an expert to say for sure. It would certainly depend on the specific form that it took, and there are a multitude of options in this space. Of the current contributors who are willing and able to push the boundaries here, I believe that quasiquotation is the preferred approach (motivated by use cases for compile-time codegen in Servo).
> I believe that quasiquotation is the preferred approach
Oh yes yes yes. Quasiquotation makes writing syntax extensions almost easy.
It can't cover everything though. For those cases, dropping down into AstBuilder (or the raw AST) isn't completely terrible, but it's a big step down from quasiquoting.
Hi! I use F# for most tasks (from websites/JS generation, to packet capture and indexing, call routing and billing processing), and some C where required. Rust looks fantastic, and would give me the memory control I need when I need extra performance. A LOT of it comes down to simply being able to stack-allocate things; in F# I'm essentially forced to use the GC heap for even the most trivial things.
Rust looks fantastic, and I'm very excited about using it. When I found out about it and started reading how it worked, it was almost exactly what I had been wanting, on almost every count. I really wish it had existed a few years ago.
My comments are from someone that's just been playing around with the getting started guide of Rust.
-- Lack of custom operators limits expressiveness. For instance, look at Parsec or FParsec. Why shouldn't they be allowed to exist in Rust? But if custom operators are totally out of the question, then what about user-defined infix functions? (And then, why not functions with nearly arbitrary codepoints as identifiers?)
-- It seems that currying, partial application, and function composition are sorta cumbersome in Rust. Is this a conscious decision, that idiomatic Rust shouldn't be doing such things? Like " add >> inc >> print " being equivalent to "|a,b| -> print(inc(add(a,b)))" ? In F# I use this kind of stuff all the time, especially when processing lists.
-- It seems there's a difference between function types. Like if I do "fn foo(a:int, f:proc(int)->int)", I can call it with foo(1i, inc) if inc is a fn. But if I first do "let f = inc; foo(i1, f)", that's an error. Offhand, I'd assume this is due to lifetime management, but it feels a bit strange. When writing HoFs, do I need to implement a version for each kind of function? Or am I totally misunderstanding things?
-- Sorta related, does Rust allow something like:
let inc =
let x = ~0
|| { let res = *x; *x += 1; res }
The idea is to expose a function that contains some private state. I remember hearing that Rust changed the ownership stuff around a few times, but the basic idea is to create a globally-accessible closure. Is this impossible, requiring us to use statics to store the global state?
-- Why doesn't Rust warn when discarding the result of a function if not unit? Is it idiomatic Rust to return values that are often ignored?
-- Even without higher kinded types, monadic syntax is useful. Is Rust planning any sort of syntax that'd let users implement Option or Async? How does Rust avoid callback hell? Or is this quite possible today with macros and syntax extensions?
-- Has Rust considered Active Patterns (ala F#)? With that, I can define arbitrary patterns and use them with matching syntax. E.g. define a Regex pattern, then "match s { Regex("\d") => ... , Regex("\D") => ... , _ => ... }"
-- Consider allowing trailing commas in declarations; why special-case the last item?
-- And last but not least: Please, please, please, reconsider type inference. It's baffling why "fn" requires type annotations, but a closure does not. What's more, why is there even different function syntax in the first place? I've heard the reason that "top level items should have types documented", but that's a very personal decision to make. It certainly isn't something the Rust compiler should force me to do in all my code. Why do ya gotta limit my expressiveness? (Same argument I'd use for custom operators.) Statics/consts should also have type inference. And note that these items aren't necessarily public or exposed - but a private fn on a module still requires type annotations.
Rust 1.0 is supposed to be somewhat minimal, so things like custom operators and monadic syntax are out scope since they can be bolted on in a backwards-incompatible way in future versions. Active patterns would probably also fall under this.
Likewise, having functions have their own private global state is not on the table, and seems to me to actually be unsafe since the function could be called at the same time in two different threads overwriting the operations done to them.
For not warning when returning non-unit values for unit returning functions, a lint could probably help there.
Type inference isn't changing. The rule is that items need to be explicitly declared and expressions are inferred. Closures are expressions while most (all?) functions are items. It's a purposeful limitation on inference based on experience with prior languages.
I understand that this is going to be hard for some to swallow, but it is explicitly a non-goal of Rust to be maximally expressive. :) New features are motivated almost solely by solutions to concrete pain points in Rust code (especially Servo). This may sound particularly Blubby, but the devs are well-versed in Haskell (and Lisp, and Scala, and ML, and...). With respect to custom operators and infix operators, they're taking the cautious approach of leaving the option open for future versions of Rust, since they can be added completely backwards-compatibly if there's significant demand for them post-1.0.
> It seems that currying, partial application, and
> function composition are sorta cumbersome in Rust.
There are two different camps in competition here. One camp wants Rust to have default function arguments as per C++. Another camp wants Rust to have automatic currying. These camps are in opposition because they both want to control what `foo(bar)` does for a function `foo` that takes more than one argument. So far the devs have resisted entreaties from both camps. Experience post-1.0 may change this.
> It seems there's a difference between function
> types.
Closures are really terrible right now and are in the midst of getting a complete overhaul to be more useful and less hacky. :) Procs won't even be a thing afterward. Please excuse our mess!
> Why doesn't Rust warn when discarding the result of
> a function if not unit?
It does warn, for any type that has the `#[must_use]` attribute. This includes the stdlib's `Result` type, which is basically Haskell's `Either` except explicitly intended to be used for error handling.
> Is Rust planning any sort of syntax that'd let
> users implement Option or Async?
I'm not sure what this means, as users can already implement Option. It's not special-cased by the language in any way.
(As for the lack of HKT, that's a hotly-desired feature for post-1.0. It's still a bit pie-in-the-sky, but the devs have acknowledged that it would be very useful to improve our error handling story.)
> Please, please, please, reconsider type inference.
Not going to happen. :) Requiring type signatures on top-level functions is so useful that even the languages that allow it to be inferred tend to enforce their presence via social pressure. In addition to providing powerful self-documentation, this vastly improves error messages. Finally, I suspect that Rust's trailing-semicolon rule (especially combined with the willingness to ignore the return value of most functions) would interact poorly with function signature inference.
> Statics/consts should also have type inference.
IIRC this isn't possible, but I've forgotten the reason for now. It certainly isn't motivated by any sort of philosophy.
While I strongly disagree with limiting expressiveness, operators, etc., and the thing about top-level functions is misleading (because a nested, private, module isn't really "top level), this is a fantastic response and helps me understand Rust a lot better. Thank you very much.
I hope things will change (esp. type inference, which while playing around is really annoying, even if I eventually end up wanting to annotate. A REPL could fix a lot of the pain.). But there's nothing out there that competes with Rust, and the C-friendliness means I can fairly easily interop with languages with more expressiveness ;).
As far as async/option, I meant something like Haskell's do notation or F#'s workflows. This allows implementation of async code without callback hell or the huge limitations of promises or whatnot. (But without HKTs, you can't mix multiple monad types within one block.)
Actually, after asking around, I must have misremembered about inference on statics being impossible, it's merely difficult. :) In lieu of full inference, there are proposals to allow statics to have the same sort of very simple inference scheme that C++ and Go have: https://github.com/rust-lang/rfcs/issues/296
Over the past few days I have been trying Julia. While I don't have much of an idea what I'm doing yet, and I'm probably doing a lot of unproductive premature optimization for the sake of exploring, I see a similar type of difference compared to R and Python where I have more experience.
What I am curious about is why you chose Rust rather than Julia. As I've understood the chatter so far, Rust is great for low level programming, and can work well as a replacement for other system level programming languages. Julia on the other hand is supposedly tailored for the type of task you are talking about, and like Rust, the chatter says Julia can be similar to C and Fortan in terms of speed.
My understanding of Rust was as a low-level general-purpose language and Julia as a technical or scientific programming language that is similarly fast.
As far as I am aware, Julia isn't that fast for string operations yet. The language has been optimized for numerical calculations but they haven't done much work on making working with strings as fast as they could. I remember reading this on a github issue at some point but I can't find the issue at the moment.
I adore that someone like Armin is so delighted to use Rust. From what I've seen of Flask and his other Python libraries, the level of consideration that he devotes to API design is absolutely inspiring.
At such an early stage in the language's history, I'm optimistic that his thoughtfulness in API design will become the baseline for the entire Rust ecosystem. In this vein, I'd also like to credit Chris Morgan's Teepee (http://chrismorgan.info/blog/introducing-teepee.html) and Sven Nilsen's Piston (https://github.com/PistonDevelopers) for devoting a great deal of energy to figuring out how best to structure Rust APIs that play to the strengths of the language. It's a process of discovery for all of us!
Don't forget Aaron Turon (employed by Mozilla to work on the design of the std library), who, among other things, wrote the "fluent" APIs for process[1] and task[2] spawning, which possibly inspired the redis-rs API Armin describes in this post.
I'm so glad that Aaron is on the core team, focusing specifically on API design and ergonomics, rather than just the language. He has definitely been applying much needed polish that we need for a slick 1.0 release.
Just wanted to also add that in an area of design with lots of strongly held subjective opinion and rampant bikeshedding, Aaron has shown himself to be incredibly thoughtful, well-measured, and diplomatic. Really a great addition to the team!
Yep, the command builder of the process module was the inspiration for the pipeline system. Lots of other inspiration came from libserialize where I got loads of the type conversion (and especially tuple code) from.
Speaking of which, Rust is probably an appropriate language to write a safe and fast WSGI dispatcher to go with Flask, or maybe a full server with an embedded Flask-like framework.
> The truth is that the borrow checker is not perfect. The borrow checker prevents you from doing dangerous things and it does that. However it often feels too restrictive. In my experience though the borrow checker actually is wrong much less often than you think it is and just requires you to think a bit differently.
I would love to see this explored in more detail. The borrow checker is, from what I can tell, one of the most important and novel parts of Rust. I'd love to see more detailed examples of where:
1. it kept you from doing something idiomatic that you knew was safe but you just couldn't convince the borrow checker of it.
2. it kept you from doing something that seemed safe, but then you realized that it was actually telling you something important.
Also:
> Even if it would turn out that the borrow checker is not sound
Wait, is this an actual risk? Is there some lingering doubt that the borrow checker is actually sound?
> it kept you from doing something that seemed safe, but
> then you realized that it was actually telling you
> something important.
One of the reasons that I know Rust is on to something is how often I've seen this scenario occur on IRC. It's great to watch someone come in with a complaint about appeasing the borrow checker, only to later realize that what they were attempting to do was actually subtly unsafe.
It's also really a pain now to get the borrow checker to be happy with boxed structures now that box isn't implemented in the compiler. You need to first borrow the whole structure, and then things within it.
Soundness bugs come up once in a while, so yeah there is doubts. I wouldn't claim it to be sound until there is at least a proof of soundness of a theoretical model of it (which you can then show the implementation matches)
There is actually a CS student working on it... from the University of Washington I think? Check out rust-redex. I don't know what the status is though - it doesn't seem to have been updated in a while.
Anyway the devs have said that whilst a 1.0 release will mean backwards compatibility for language features, they will fix any soundness bugs that come to light.
Searching[1] a data-structure (like a hashmap) will return a borrowed reference to that data, which "freezes" the hashmap while the borrow exists. And, due to lexical borrowing, it is frozen even if there was nothing (that is, if the return value was `None` which contains no references linked to the original map).
The following is currently illegal because the `insert` is trying to modify the borrowed map.
match some_map.find(&a_key) {
Some(x) => println!("reference to value {}", x),
None => {
println!("not found, inserting instead");
some_map.insert(a_key, some_value);
}
}
There is a Drop trait that has one method, drop(). It's like a destructor. It's called for you, but you can also call it yourself.
fn foo(x: &something) {
// stuff
x.drop();
// more stuff,
} // if we didn't drop, x gets drop()-ped here
If we had non-lexical borrows, after we drop x, we could have it be un-borrowed. currently, x is still considered borrowed until the end of foo(). This is sort of a contrived example, but if you check out the ticket it has better ones.
I have a huge hatred towards both the STL and boost and that has existed even before I worked in the games industry.
I really don't understand why STL gets so little love. It's a little lean on features maybe but in my experience it just works, and it's fast. Once you get your head around the iterator concept it's pretty simple to use. And with lambdas in C++ I can write code that's almost as concise as Ruby or Scala.
Any strongly-typed, flexible collection & algorithm library is going to be complex and require a learning curve.
In my experience the people who have strongly negative opinions specifically about the STL have it rooted in one of these two things:
- They haven't used it much in a very long time and have very bad memories of tracing through the completely opaque SGI STL implementation and its derivatives.
- They include std::string and/or std::iostreams as part of the STL, neither of which are part of the original STL and have all sorts of weird warts and poor interactions with the generic algorithm-focused core parts.
Not to say there aren't other reasons people dislike it, but these are big ones.
Probably true. I wasn't much of a fan either when I first used it in 1996. It was extremely verbose, slow to compile, and tended to generate compilation error messages that filled the screen.
But the experience of using the STL today with the niceties of C++11 and a good compiler like Clang is vastly better. I can now write code almost as concise as Ruby that runs 100x faster, without usually having to worry about memory management. I avoid using the streams stuff though. That chunk of the library could use a serious overhaul.
I'm rooting for Rust because it's designed to address the problem spaces I care about most but C++ has improved by leaps and bounds in the last few years.
Yeah, Clang has really improved the ecosystem as a whole, as has this decade's updates to the language and libraries. But unfortunately they are all band-aids over some very deep foundational problems that were decided decades ago (I don't blame Bjarne by the way - he didn't have the benefit of hindsight).
I am glad we have a new language that takes the best from C++ and sets in on reasonably solid theoretical foundation inspired by languages like ML and Haskell. It's not watertight yet (unlike the formally verified ML), but it is damn better than the current status quo.
Lambdas in C++ are pretty new. Most of us who used std::algorithm had to write our own functor structs and do the variable capture by hand. Except that typically we didn't, because at that point a for loop is more readable. I think that's the essential objection to the STL: it's a library built around functional programming and not OO, in a language built around OO and not functional programming.
And of course there are a bunch of small dumb things in it. Why do we have make_heap() and friends instead of a heap type? Why does binary_search() return a bool instead of an iterator? Why didn't we get any hash tables until like 2010? Who thought the vector<bool> specialization was a good idea, and why didn't they benchmark it first?
The STL is a weird mix of brilliant, visionary API design and abject failure. Because of C++'s many limitations, the fail-y parts are the parts that most people are most familiar with. C++11 has done an incredible amount to turn that around, but at this point, for me, it's too late: I'd rather have a new language and new library, informed by C++'s successes without being bound to its mistakes.
The STL is a weird mix of brilliant, visionary API design and abject failure.
Sounds like most software projects. IIRC the Scala folks have pretty much rewritten their entire collections library twice already, and that's with the benefit of hindsight.
I'd like to see a mature new language in this space with a good ecosystem too but the number of major swerves they've taken in the Rust design already makes me inclined to think that, like C++, they're going to discover they made some serious mistakes along the way.
I see what you mean but I do think the STL was more visionary, and more abject, than almost anything else in its reference class. Truly unified sequence operations, for example, were novel back then. On the other hand, binary search _wasn't_; I'd love to know what that guy thought he was doing.
The game industry does have somewhat unique requirements so I can understand why they'd need something more tailored. On the other hand that paper was written 7 years ago, which is ancient history now. For general purpose stuff I've found the STL works just fine, even in the realtime DSP stuff I've been working on.
The biggest problem I've had working with the STL is that people avoided it for a long time so when you're dealing with third party code you usually also have to deal with custom vector, string, list etc. implementations.
I rarely have to do anything as low-level as rust would demand. More often than not I find myself using Python, but I had the same feelings when I started looking into rust a few weeks ago. It was exciting seeing the full pattern-matching, especially.
I have a desire to get some experience in the area, but the problem is what to make. Does anyone have any ideas for some smallish libraries that would benefit the community? I'm certainly not a networking or concurrency expert, so it's difficult to come up with ideas.
What kind of areas are you interested/experienced in? What would you like to learn? I'm sure there are tons of gaps out there that you could help fill. Maybe the folks on irc.mozilla.org #rust might be able to give you some ideas.
Thanks. I'll definitely try out irc for some ideas. My general projects are small automation tasks for myself as well as small web-based apps for myself and friends. That's part of why coming up with ideas for something like rust are a little tricky.
The only promising thought I've had is maybe implementing some sort of template preprocessor that I can find a way to work into an existing rust framework.
Yes. Rust borrows quite a few things from the ML family of languages (and things unique to Haskell like "deriving"). OCaml has the advantage of offering very good performance while not having to deal with the borrow checker, but it doesn't have the momentum behind Rust (the community is quite small, and the number of libraries isn't that large). That said, it is a very nice language to work with, and offers a number of features absent from Rust, such as a great module system, structural typing, first-class laziness...
That's true, though through Lwt (and Core.Async) it has good support for concurrency. However, the work on the parallel runtime seems to progressing at a steady pace, so parallelism is coming.
That's a good question. I think F# and Haskell have stolen a fair amount of OCaml's thunder. Meanwhile, Rust caters to everybody who is tired of C/C++ (putting D in the same position as OCaml, I guess?), while having nice enough constructs that people coming from high-level languages get interested in it. By contrast, OCaml doesn't have the "shiny new" feeling of Rust (also suffers from other issues, such as its standard library/libraries, and the tooling, and the "mlis are documentation" attitude that is fairly prevalent in the community). I'd say with more polish and a lot of marketing, it could easily gain more market share (even though some things have gotten much better since opam).
Sometimes, working in OCaml feels like flying a starship equipped with XVIIIth century cannons. The awesomeness of many of its features is in stark contrast to the gaping holes of its ecosystem (no good INI parsing library in 2014? no usable pure OCaml regexp library?).
Unless you are using CPython extensions, PyPy is usable as a drop-in replacement that's significantly faster in almost all cases. In order to notice any significant speed gain, the JIT needs a chance to "warm up" though, so on short running applications or on web applications for the first couple of requests PyPy can be slower than CPython.
> Does OCaml still doesn't compile 1.0 + 2.0 or has that changed?
I'm not quite sure what you mean? In any case, the compilation speed isn't really an issue (especially since we're talking about Rust, which last time I used it wasn't exactly fast).
Yeah. Honestly it's a quirk, but I prefer quirks going this way than the other way (like universal compare). That said, all languages have warts. And that's the best kind of warts, the one that is explicit and happens at compile-time.
I've been thinking about implementing fastcgi so that I could play with simple rust webapps. Haven't had time to start on it though, so if that sounds interesting you'd have at least one immediate user.
Used more hours than ever expected on it. Simple put you'll fall in love with the compiler, because it point out so well your mistakes.
Another thing to note is that it greatly encourage you to fix your warnings too. which for me coming from java and php world isn't something you'd give much thought.
I agree about the warnings. Normal Rust code is already so strict that a lot of the warnings that you'd get from other languages are already compiler errors, which means that issues that are merely good style (e.g. declaring a variable as mutable when you never actually mutate it) can spit out warnings without overwhelming coders and causing them to lapse into "I've already got 200 pages of warnings so screw this" apathy. It's a fine line though, and something we have to remain mindful of as we add more warnings by default.
Rust actually makes me think a lot about Ada, if you want to program, you need to program correctly.
Probably the reason I will not use Rust is the same reason I only look at Ada in a professional context, I usually start projects with something quick and dirty, then gradually refactor it into something better, rather than piece by piece. Being forced to do it right the first time would likely just push me more into doing less programming.
No doubt clang has been a good influence in setting the standard for gorgeous, readable error messages with nice colored squigglies and arrows. Borrow check errors probably the hairiest you get, and you get less and less of those as you internalise the rules.
I've been very interested in Rust for a while, but put off by the early development status and the fact that it's undergone a few major redesigns. I don't hold them against the language team in any sense, and actually think more of them for it, since redesigns early are good if you realize something else might be better or one of your original ideas just didn't work out.
Rather, I'm just curious when I can expect Rust to settle down and not have frequent breaking changes, major redesigns of language features, etc. I've heard it's the aim of the 1.0 release to have this property (which is a reasonable goal in my mind).
Does anyone have a timeline on when it would be reasonable to start eyeing Rust if I want more stability (in terms of language features) than it's had during its development phase?
There was a blog post recently discussing what is left before a 1.0 release, and the timeline for it. I'm in the same boat as you and the blog post has been helpful because now I can at least feel comfortable learning some basics, while avoiding the areas that are still in flux.
The `try!` macro unwraps the okay part of the expression and early returns the err part. The nice thing about the explicit results is that you will do the error handling. With Python you often do not know if an error might come.
No, but that's would also defeat the purpose. The idea of results is that you know from looking at the code what can fail. If you remove that information you might as well use exceptions.
You need to handle all results in Rust (because the second option is the error case), or the linter will complain. Considering that failing the task (like in e.g. Erlang) is the standard way to handle errors, yes, this is somewhat idiomatic.
Failing the task isn't the standard way to handle errors; propagating information via the Result type (especially the try! macro) is more conventional, as it allows the caller to handle the problem, failing is very annoying to recover from.
Python made the choice to sacrifice performance for much better readability, and it shows. You can see the difference between code elegance when working with strings in either languages, for example. The downside is that Python's benchmark numbers are poor. Rust can get to within 1x C's performance; Python would be lucky to get within 100x.
But I do share your concern about `unwrap`. Is it idiomatic to do `unwrap`s, or is it better to define functions to accept and return Option<xxx>?
Your main function is most likely pretty empty and that's the only place where you need to deal with hard failure. And that's the same in all languages. If you write in Python you don't just not handle exceptions either. Somewhere you catch them down and present them to the user in a good way.
What do you mean by 'vanilla ML'? I wasn't sure there were any really around... Ocaml has tons of stuff added, and SML also has lots of added extensions.
OCaml would be fine. It's a good language with a very efficient run-time.
I'm arguing that you could throw out most of the advanced features and that would be enough for most people. Its best features are the core ones. That's why I don't really care which specific flavor of ML you pick. Any would be a big win over many languages in wide use today.
It can take some wrangling to avoid space leaks and you often have to be careful about strictness (especially when a small change in the code causes the compiler to miss one of the big optimisations), while languages like Rust and C++ put more control into the hands of the programmer and thus achieve high performance more naturally.
As an example of this, consider the benchmarks game[1]. The Rust and Haskell are on par[2] for the most part.
Except, the Haskell programs are having to pull out all stops to reach this level, they essentially all have a ton of strictness annotations, and are doing manual pointer manipulations and even manual allocations. Of the fastest Haskell solutions (i.e. the ones linked on [2]) I count two that don't use a `Foreign.*` module (pidigits and binary-trees) and only one that uses no !'s (pidigits).
On the other hand, none of the Rust programs use any `unsafe` at all (that is, they are guaranteed to be memory safe, e.g. no risk of out-of-bounds accesses or dangling pointers), and are generally not particularly optimised (e.g. the Rust program that is 85 times slower than the Haskell just appears to have been written without thinking about performance at all... it does a whole formatted printing call for each and every character in the output! The version in-tree[3] is at least 50 times faster and still uses no unsafe code).
(I say this as someone who likes Haskell: I'm the person with the most votes for answers in the [rust] tag on StackOverflow, but I've got even more votes than that for my answers on the [haskell] tag.)
It's true that Rust approaches the "if it compiles, it works" property that Haskell has, but otherwise the two languages have very little philosophical or practical overlap. It doesn't make sense to compare them outside the context of "here are examples of programming languages with relatively strong type systems".
Well, Rust has some kind of typeclasses, #deriving, stuff like that. If what parent means is "if you like the things Rust borrowed from OCaml and Haskell, check out Haskell", it's not unreasonable, especially if addressed to people coming from mostly-imperative languages (C or Python).
I agree that Rust and Haskell do feel mighty different in practice.
(Warning: not at all a Haskell expert, so please correct me if I'm wrong.)
Each language's deriving facilities aren't actually comparable, as Haskell's are actually generic whereas each Rust's are all hand-coded syntax extensions.
Also, I believe that it's possible for Haskell code to fail at link time due to libraries providing incompatible typeclasses (contributing to Cabal hell, perhaps?), whereas Rust traits have different restrictions such that if your library compiles, you know that your users will never experience failure due to the traits implemented by other libraries.
So even though the Rust devs have clearly taken some degree of inspiration from Haskell, there are enough subtle interactions with other systems that even the familiar features have a distinctly different feel.
I will say, though, that in my observations there have been more than a few people from iterative backgrounds who have said that familiarity with Rust was a useful stepping stone to Haskell. So even though I'd hesitate to compare the languages in absolute terms, in relative terms it's true that Rust is probably the iterative language with the most to offer to aspiring Haskellers.
What do you mean by Haskell's deriving being generic? As far as I know, Haskell can only derive a fixed list of classes, except for newtypes, which are sort of a different case. How the fixed list is implemented in the compiler isn't that important... on the contrary, at least vanilla Rust has a way to define arbitrary deriving-like extensions based on inspecting the structure at compile time (even if it's somewhat ugly), while vanilla Haskell has nothing of the sort.
> it's not unreasonable, especially if addressed to people coming from mostly-imperative languages (C or Python).
Perhaps not, but how it is brought up (and worded) feels a bit dismissive of Rust. Which would be fine, if it were not for the fact that the topic of the thread is Rust, and "try Haskell" comment is very drive-by.
I agree with you, but Rust is mostly targeted at C++ developers so it makes sense that they'd use C++isms where they it wasn't crazy. Examples of crazy being resource declaration mirroring use.
Disappointing how this author's hyper-criticism of Python is not applied equally to Rust, presumably because Rust is newer and this author has other bones to pick with Python. If Rust has a problem, he will sell that it is charming (e.g. "it makes you think", "you just need to think differently", "needs more love"). The glass will be full any day now! But if any new effort in Python is not absolutely perfect or just not to his personal taste, he will sell that it's because Python is a horrible pile of shit because nobody listens to him. The glass isn't only half empty, it is also too hard, too brittle, too cold and actually also too warm and it turns out that not even its transparency is a good feature.
After several years of this routine, it has become very old. For God's sake, if your ex is so bad then just move out of her house already. Don't just keep eating the dinners she prepares and complaining about them and saying you'll move out. Actually move out and stop talking about her all the time. Yet another article about how you can't stand how she clips her nails is not helping anyone.
The criticisms of Python covers about one sentence, and they're all to the point. I don't see how you can turn a well-written post 99% about Rust into "hyper-criticism of Python".
> he will sell that it's because Python is a horrible pile of shit because nobody listens to him
I'm not sure we're talking about the same Armin here...
> , presumably because Rust is newer and this author has other bones to pick with Python.
Presumably because this attitude is what you've seen from the author before? Or presumably because you just assume that he is a "Magpie Developer"? If its the latter, that seems like a really unfair presumption.
1. Writing Rust code definitely takes more time than Python or Ruby, but it's not bad in practice. I can't measure the productivity difference yet, partly because I'm still learning Rust. I do spend more time thinking about how to write zero-allocation and zero-copy algorithms, and trying to get my code to compile (but see 3, below).
2. It's possible to write very fast code without compromising memory safety. It's great to see key parsing operations happening in ~100 nanoseconds, with no allocations.
3. Rust is one of those languages where I need to work to make the compiler happy, but once I manage that, the code generally works on the first try.
4. The tooling is very good: library management, unit testing, benchmarking, documentation, error messages etc., all work very nicely.
Overall, it lacks the instant gratification of a really good scripting language. But I keep finding myself back in Emacs, writing more Rust code for the fun of it. And I really enjoy the combination of low-level code, memory safety, and a useful selection of functional programming features. I'm glad to see a new language in this niche.