A Fresh Look at Rust
lucumr.pocoo.org
lucumr.pocoo.org
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.
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.
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.
Or to an old mirror of the official doc, http://web.mit.edu/rust-lang_v0.9 is the bane of my rustperience.
I did learn something about metals, though, so there's that.
But I totally understand that you probably have other, well thought-out reasons not to do that.
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.
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).
The borrow checker looks really valuable though, and something that might have a big influence on language design going forward.
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.
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.)
You can take a look at the syntax extension here: https://github.com/rust-lang/rust/blob/master/src/libregex_m...
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.
(There are quite a few tricks employed to get native and dynamic regexes to have the same API. It's the reason why the main `Regex` type is actually an `enum`! https://github.com/rust-lang/rust/blob/master/src/libregex/r...)
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).
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.
They are allowed in most circumstances.
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.
> Lack of custom operators limits expressiveness.
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.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.)
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.
[1] - https://github.com/BurntSushi/rust-csv
The docs explain the different access patterns (start with convenience and move toward performance): http://burntsushi.net/rustdoc/csv/#iteratoring-over-records
+ - Still working on gathering evidence...
Note: CSV is actually much more complicated in practice than just splitting on commas.
1. Sometimes you want to actually write files, in which case you'll want help escaping quoting.
2. Shortcuts for using header information to provide more convenient access into a particular column, rather than always doing it by index.
3. Conveniently slurp in only parts of a file, or only particular columns in a file, without loading everything into memory.
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.
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.
http://programmers.stackexchange.com/a/215171
CSV is not just simple fields separated by commas with some quotes thrown in.
Yeah.
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.
I like the word "almost". It's kept me in cheesy-puffs for years, now. :-)
I was actually only speaking to the post I replied to. CSV is a mess.
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.
Anyway, I'm just curious about the trade offs.
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!
[1]: http://doc.rust-lang.org/nightly/std/io/process/struct.Comma...
[2]: http://doc.rust-lang.org/nightly/std/task/struct.TaskBuilder...
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?
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.
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);
}
}
#6393 is the relevant issue.[1]: http://doc.rust-lang.org/nightly/std/collections/struct.Hash... [6393]: https://github.com/rust-lang/rust/issues/6393
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.There's also good examples here: http://www.reddit.com/r/rust/comments/2hy06n/a_fresh_look_at...
> 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.For the former point about things that the borrow checker doesn't have enough information to allow, see http://www.reddit.com/r/rust/comments/279yw3/borrowing_from_... for an example.
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.
- 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.
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.
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.
Well, in light of the fact that the author makes reference to his game industry background, you may find interesting the perspectives given in this paper: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n227...
And partial source code for the library described in the paper: https://github.com/paulhodge/EASTL
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.
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.
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 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.
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.
> OCaml has the advantage of offering very good performance
> while not having to deal with the borrow checker
My understanding was that OCaml had no support for parallelism -- so a single core is all you get. Is that not the case?The biggest issue is that the runtime still has a global lock, but it is in the process of being removed.
I believe there are serious efforts towards removing the GIL these days.
How come, given the amount of use it gets in the industry and academia versus Rust that still hasn't reached 1.0?
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?).
Small stuff like this really bother some people and they fly off to Pythonland never to be seen again.
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).
Personally I never used Python for anything other than glorified shell scripts and Websphere installations (after Jacl got replaced by Jython).
For everything else I prefer languages with type inference and availability of native compilers (AOT/JIT) in their toolchains.
How good is PyPy for production code nowadays?
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.
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.
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.
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?
.unwrap_or_else(|e| { fail!("well, that's unexpected") })
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.- http://doc.rust-lang.org/nightly/std/result/
- http://doc.rust-lang.org/nightly/std/result/#the-try!-macro
In the example script, it really doesn't make sense to go on after that.
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>?
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.
Or at least have some way of saying " if there's any error in this block of code, simply stop execution and forward it to the calling statement"
I still think some kind of vanilla ML would suit a lot of people very well.
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.
- ex-Python and Clojure user, teach Haskell now.
I agree that Rust and Haskell do feel mighty different in practice.
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.
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.
That's not true at all, cf. SYB and Generics.
Haskell has very little syntax in comparison to something like C or Python and what little is has is very obvious.
I'd much prefer ML/Haskell-ish syntax in Rust, but I know I'm in a minority.
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.)
[1]: http://benchmarksgame.alioth.debian.org/
[2]: http://benchmarksgame.alioth.debian.org/u64/benchmark.php?te...
[3]: https://github.com/rust-lang/rust/blob/master/src/test/bench...
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.
> 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 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.