Two Weeks of Rust
matusiak.eu
matusiak.eu
It certainly is.
The advantages over C++ seemingly never end. I'd add to the list: rigorous portability, non-broken macros, ADTs and pattern matching, a careful language stability protocol, standard test and doc tooling, flexible namespacing via visibility and modules, the absense of vast lacunas of undefined behaviour, simple numeric promotion rules, meaningful compile-time errors for generic code. Plus things already mentioned in the post like Cargo.
People tend to focus on the safety side of Rust, but as impressive to me is just the... solidity on all these other axes. If you stripped out lifetimes and the borrow checker you would still have an incredibly compelling alternative to C++.
Im not trying to be a jerk. Just voicing my concerns. Learning a new language and producing code in it is a huge commitment. It better be around in 10 years. Will it still be as clean and simple, or will it have grown complex. What if Mozilla stops sponsoring it?
C++ is 35 years old. It has stood the test of time. I would not be surprised if it was still popular in 35 years time, when I retire. There is so much C++ infrastructure code in this world, that its not going anywhere.
In the meantime C++ has evolved. I cant remember the last time I had an uninitialized memory, null pointers, or buffer overflow -bug. Those are low level C problems. If you avoid C-style programming and stick with modern C++ (RAII, value semantics, STL, boost, etc) you wont get them.
C++ is changing fast. In the next few years we are getting high level concurrency and parallel language support and libraries (no more low level thread problems), we are getting modules (no more preprocessor hacks), compile time reflection (no more manual serialisation), concepts (no more ugly error messages), ranges (no more cumbersome iterators), and a whole lot more.
And finally there is "C++ Core Guidelines" which aims to be checkable by tools. So you get a warning when you are relying on undefined behaviour.
I think C++ is still the future.
And those other languages have slowly eroded C++'s dominance. You have to remember that in the early '90s, C and C++ had nearly 100% market share for all applications. That dwindled with the rise of Java, then dwindled more with the rise of dynamic languages, and the mobile/modern era made it dwindle even more with Objective-C, Swift, and Go. Now C++ is a popular language for a certain class of applications, but by and large new programmers aren't even learning it anymore. That's a huge shift.
> In the meantime C++ has evolved. I cant remember the last time I had an uninitialized memory, null pointers, or buffer overflow -bug.
I do. https://bugzilla.mozilla.org/buglist.cgi?query_format=specif...
> And finally there is "C++ Core Guidelines" which aims to be checkable by tools. So you get a warning when you are relying on undefined behaviour.
As I've said before, I have questions about this tool: how much like C++ it will really be after ruling out so much code, and how they plan to statically guarantee noalias on function boundaries in the presence of shared_ptr.
But C++ also has some domains of its own where it is the king. Systems programming and any application where performance is a priority.
If C++ started slipping against a new languages in those areas, then it might be a sign of the end for C++. I don't think its going to happen for a very long time. Its hard to beat C++ when it comes to performance (while still having high level abstractions). And any general purpose language needs to know how to do "a bit about everything", so it would probably be just as complex as C++.
(Even though C++ has slipped in percentages, I bet there are no fewer C++ programmers today than there ever was, there's are just more programmers in general.)
If something is going to kill C++, its probably a language that scales very well to multicore. Maybe a functional language. I be happy if that happened as that would be a revolutionary breakthrough.
On you final points. I think C++ programmers are slowly but surely moving away from C-style coding towards modern C++ (and we will see less of those types of bugs). It takes time though. Id be happy if C++ at some point was sub-setted and the worst parts of C where deprecated (Thats the other aim of "C++ Core Guidelines" as I understand it).
About the aliasing problem. I'm not sure how it could detect that either. We will probably have to live with it. In general though smart_pointers are still just pointers, and its better to avoid them when one can (and use value semantics instead).
http://www.tiobe.com/index.php/content/paperinfo/tpci/index....
Re: performance, Rust's doing pretty well (on a bunch of arbitrary microbenchmarks):
https://benchmarksgame.alioth.debian.org/u64q/compare.php?la...
Re: high level abstractions, Rust doesn't have many of the template shenanigans C++ does (but that's a good thing, IMHO). However Rust does have a phenomenal type system for a language with such predictable and solid performance. And it has many wonderful high-level abstractions (exhaustive pattern matching on algebraic data types, iterator syntax for collections, many functional idioms, an AST-based macro system, etc.).
> If something is going to kill C++, its probably a language that scales very well to multicore.
Rust has fantastic concurrency, and the libraries for it are still in relative infancy. The type system keeps un-synchronized shared mutable state from happening (unless you intentionally turn off certain checks using unsafe code, IIRC). There are freely available libraries for lock-free data structures and stack-scoped threads. As an aside, it's also really easy to use those libraries without having to vendor some random header files that may or may not conform to your coding style.
> Maybe a functional language. I be happy if that happened as that would be a revolutionary breakthrough.
Rust also has some pretty cool functional syntax, although I'm not an expert on functional programming, I've been enjoying it quite a bit.
> About the aliasing problem. I'm not sure how it could detect that either.
Not sure how C++ intends to do it, but Rust does it with a strict ownership system that is fully enforced at compile time.
On the benchmark though, its doesn't seem quite fair. At least that first one is comparing a single threaded c++ program, to a multi threaded rust program. And for the regex example the author doesn't use C++ std::regex and std::futures (std::async) like he does for Rust. It makes me think a proper C++ implementation would do much better. (Personally I use the Parallel Patterns Library. I like it a lot. We will get something similar in C++ in a few years)
I don't know much about rust. It worries me though the examples are still using threads and mutexes. I think we need much higher level abstraction (like ppl::task, coroutines, etc) to get better scaling. Also lock free data structures dont scale that well either. As they still require synchronization and that hurts scaling). I think we need some kind of revolution in how we code to scale our programs to hundreds of cores.
> And for the regex example the author doesn't use C++
> std::regex and std::futures (std::async) like he does
> for Rust.
Rust's standard library implements neither regexes nor futures, so the code you're seeing must be coming from third-party libraries. If anything, this comparison would favor C++, since its own libraries are likely to be more mature and optimized than Rust's. > It worries me though the examples are still using
> threads and mutexes.
You shouldn't be worried. :) C++ may require higher-level abstraction to make concurrency tenable, but Rust was designed as a concurrent language from the outset. Rust's type system prevents data races at compile-time, so programming with raw threads isn't nearly as fraught as it is in every other language and refactoring concurrent code can be performed with compiler-assisted confidence. I recommend Aaron Turon's blog post "Fearless Concurrency with Rust": http://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.ht... > Also lock free data structures dont scale that well
> either. As they still require synchronization and that
> hurts scaling
This is another assumption that I suspect that Rust obviates. Let me recommend another of Aaron Turon's blog posts, "Lock-freedom without garbage collection": http://aturon.github.io/blog/2015/08/27/epoch/That was the dream of the mid-2000s. But now hundreds of cores are never going to happen. The CPU vendors have decided that the industry has run out of time to parallelize their programs and are now refusing to scale up. Our hope for speedups now lies in using SIMD and GPUs effectively ("heterogeneous computing").
I'm not sure about the details for the "fasta" benchmark, but I know it's at least partly I/O bound, and many implementations for the benchmark are single-threaded:
https://benchmarksgame.alioth.debian.org/u64q/performance.ph...
Notably, Rust also edges out a multi-threaded C implementation for that one. I'm sure they've done some very hairy optimizations to get to the top on that board, but it's cool to see that it's possible.
On the subject of regexes, I am not familiar with the C++ std::regex implementation, but it does look like (some rather slow) C++ implementations are using a boost regex library. Those are generally fairly "standard" in C++, right?
The most important optimization I did was to avoid regex machinery as much as possible. In particular, Rust's regex library has very good support for prefix literal optimizations. In particular, regexes like `abc[x-z]foo|bar` will compile down to an aho-corasick[1] automaton over the strings `abcxfoo`, `abcyfoo`, `abczfoo` and `bar` with the failure transitions completely evaluated. (The end result is an automaton represented by a matrix. This is memory intensive, so only prefix literals of a certain size can be accommodated. But you don't need a lot to realize huge gains!)
I wrote more about it here: https://www.reddit.com/r/rust/comments/39unje/ahocorasick_fa...
Hint: most of the benchmark game regexes are relatively simple and compile down to either simple `memchr` calls (most regex engines will do that, nothing special) or a aho-corasick DFA that completely avoids the traditional regex evaluation machinery.
In general, regex implementations differ dramatically in the optimizations they perform. It's hard to draw conclusions about them without some explicit knowledge of how it is implemented.
About the regex example.
https://benchmarksgame.alioth.debian.org/u64q/program.php?te...
The boost regex library is fast and widely used. It doesnt look like the example is using it though, but a library called "re2". Never heard of it before. Googling turned up this https://github.com/google/re2
> any general purpose language needs to know how to do "a
> bit about everything", so it would probably be just as
> complex as C++.
Unless C++ massively breaks backwards compatibility (which would be a death sentence for the language, IMO), new general-purpose languages will be capable of achieving an order of magnitude less complexity than C++ simply by learning from C++'s history and avoiding all of its unfortunate missteps. Rust is one such language, having studied C++ extensively. > If something is going to kill C++, its probably a
> language that scales very well to multicore.
Rust was conceived in order to write a browser engine with extreme fine-grained concurrency and parallelism, at every level, and capable of scaling from one core to as many as you can throw at it. See https://github.com/servo/servo/wiki/Design for a basic idea of its structure. > I think C++ programmers are slowly but surely moving
> away from C-style coding towards modern C++
This is not my experience. As far as I can tell, C++98 remains the most popular dialect of C++ used in a day-to-day capacity in industry. Even among the relatively "hip" Hacker News-ish crowd, I have seen just as many people advocating that C++ usage revert to the "C with classes" approach as I have seen people advocating modern C++. > [TIOBE link]
Even if you accept TIOBE as an authority on language popularity, you need only scroll down to the historical graph to see that C++ has eroded heavily over the past 15 years, whereas Java and C have more-or-less held steady. Even among the tier of languages that C++ leads now, PHP, Python, and C# have all held historical market share greater than C++ does today, which to me indicates that we are past the point in time where C++ can be said to hold a commanding position in the market. And even disregarding that it has Java and C to compete with, it would supremely difficult for C++ to regain a commanding market position now that it has to compete with new languages on multiple fronts where it formerly dominated: systems programming (Rust), server programming (Go), application programming (Swift).So no, C++ isn't going to "die" (what language ever truly dies? COBOL still powers all the world's banks, and I've personally been paid to write RPG-LE), but I wouldn't bet on its ascendancy.
While your small walled garden within C++14 is nice, clean, safe, and well maintained. Massive parts of it are built ON THE VERY FOUNDATIONS YOU ARE ATTEMPTING To AVOID.
This is the irony of C++. While there is a very nice subset of the language, that does nearly all the same things Rust does. You still have 30 years of legacy laying around. You likely won't find a job that EXCLUSIVELY follows the C++14 guidelines. You WILL have to learn the legacy patterns, and work with legacy code, and make legacy mistakes.
>C++ is 35 years old. It has stood the test of time. I would not be surprised if it was still popular in 35 years time, when I retire. There is so much C++ infrastructure code in this world, that its not going anywhere.
I don't debate this fact. But legacy C code never stood in the way of C++'s adoption. There are always gonna be MORE programmers tomorrow, then today. People adopting a language is hardly a zero sum game.
Large existing codebases are a strength of C++ though. Any new replacement language won't magically make those codebases go away.
> [...] But legacy C code never stood in the way of C++'s adoption. There are always gonna be MORE programmers tomorrow, then today. People adopting a language is hardly a zero sum game.
The killer feature of C++ was the seamless interoperability with C which allowed trivial use of C libraries, and, most importantly, easy piecemeal evolution of large C codebases.
For example GCC is begin ported to C++ with relative ease, while a rewrite in rust would be a significantly more complex endeavor.
Then again, beyond lifetime checking, rust and C++ are very similar languages and seamless interoperability is not inconceivable. I believe that's the missing feature for rust to achieve world domination.
Which I already said...
>>I don't debate this fact. But legacy C code never stood in the way of C++'s adoption. There are always gonna be MORE programmers tomorrow, then today. People adopting a language is hardly a zero sum game.
>For example GCC is begin ported to C++ with relative ease, while a rewrite in rust would be a significantly more complex endeavor.
Source or opinion?
>Then again, beyond lifetime checking, rust and C++ are very similar languages and seamless interoperability is not inconceivable. I believe that's the missing feature for rust to achieve world domination.
No I agree. Seemless importing with C++ would be amazing. Name mangling in C++ is just a bit of an issue. Rust works great with C. The FFI is very stable. I use Rust libraries at work as generating 32bit DLL's is possible with nightly.
It doesn't matter that there's much legacy C++ code. Two things will happen:
a) it will stay C++03, regardless of what changes in C++ or what other languages pop up. This kind of code is probably part of a working product which fulfills its mission and maybe gets small updates or fixes.
b) it will continue to be maintained, and it will be able to benefit from some or most of the changes coming in new C++ versions. It doesn't mean that it will be converted to super duper C++14, but that's not how software maintenance works anyway! And guess what, they probably won't be converted to Rust either, but they will be better due to the new standards. You are building a strawman when claiming that it's either Rust or failing to use pure C++14. Successful software tools offer backwards compatibility when making improvements and iterate instead of replacing everything. Just ask the Python and Perl teams.
And this is how C++ was able to build marketshare, by offering a transition path from C. What exactly is that path for C++ to Rust, rewriting everything?
Finally, new projects will use the new standards. My current project is C++11. The next one will be C++14 and we will absolutely make use of the new features where it makes sense.
What we need is memory safety, and new standards don't provide that. Maybe in the future they will, but I have significant questions about how suitable the ISO Core C++ lifetime checker will be.
> Just ask the Python and Perl teams.
Python and Perl were not backwards compatible with anything when they were released.
Rust is not a new version of C++. It is a new language.
> And this is how C++ was able to build marketshare, by offering a transition path from C. What exactly is that path for C++ to Rust, rewriting everything?
No. It's a very comprehensive FFI, IMO among the best in any language. We even have (preliminary, quite hacky) C++ support.
We had to spend time developing this, because we actually use Rust for Servo and we had to develop incrementally, retaining large C++ components. It works great.
> Python and Perl were not backwards compatible with anything when they were released.
I think the implication is that the teams will tell you that the upgrades to Python 3 and Perl 6 were problematic because they didn't offer backwards compatibility. If so, it's a bit silly, because the point of those upgrades was to advance the languages beyond would could be done with backwards compatible iteration. Asking the teams would probably yield a bunch of people that were adamant that the decision was sound, and it needed to be done, but there were missteps in execution. Asking the communities would likely yield a more polarized set of opinions.
That said, IMO using either of these items as examples to bolster an argument is almost always a mistake, since their own discussions are so large and polarizing, they rarely ever make make a point more clear.
Perl 5 (and older) was highly backwards compatible from a programmer standpoint. Much of Perl is an agglomeration of preexisting Unix command line tools and little languages into one language.
[1]: http://www.arl.wustl.edu/projects/fpx/references/perl/learn/...
If Rust is highly semantically compatible with C++, then automated porting of C++ to Rust isn't such a far-out idea.
:.:.:
I don't have an issue with C++ itself, its just the meme that "C++14 will solve all of C++'s problems". It won't. C++'s problems are now locked in, and fixed. They've been known for 25-30 years, and we'll deal with them for another 50+.
Yes the tooling is great, there are amazing libraries, there is still virtual templated necromancy, there are still null pointers, and use after frees. There still will be tomorrow, and there still will be in 50 years.
Anyway, I think your strategy is sound. Rust is a young language and I would be pleasantly surprised to see code written today still compile in five years. I got bit by this with Swift v1 in a little app. When 1.x came, it failed to correctly transform the source and the whole thing was a major PITA to update. Completely soured me from using Swift.
I can understand why Mozilla wants to use Rust. It solves a problem for them, they largely control how the project evolves and can plan for it. To me, the big questions are what will happen to Rust if Mozilla changes strategies (Persona, Thunderbird, FirefoxOS) and whether Mozilla will continue to be a successful organisation. Personally, I root for them, in spite of their recent missteps.
I think Rust might be a good choice now for language aficionados and early adopters which can afford to waste time in exchange for other potential benefits. I would absolutely not use it to bring a project to market or for an OSS project which has a long term vision. In 5+ years I expect such a decision could be worth revisiting.
We have had a code stability promise for months now in 1.0. So what you are saying here is that you do not believe that Rust will adhere to what it very publicly planned to do. Do you have specific reasons for this?
> To me, the big questions are what will happen to Rust if Mozilla changes strategies (Persona, Thunderbird, FirefoxOS) and whether Mozilla will continue to be a successful organisation.
Rust has a core team and community that overlaps a lot with, but is very much not identical to, Mozilla. Probably most Rust users aren't even Firefox users.
Rust is not "Mozilla's language". It's the Rust community's language.
Since 1.0, Rust has in fact had multiple significant stable-breaking compiler changes, not to mention that, among other things, adding any new method to a trait or type in the standard library has the potential to cause breakage. The official code stability promise has not been violated by these changes only because it's riddled with exceptions.
This is essentially the same as any other language. Adding a new method to traits or types can also break JavaScript, the other example you gave.
In fact, I know of one such example where code was broken in the wild because of new methods being added, and we did not revert the change. Marijn Haverbeke's winning js1k entry relied on the 4th and 7th letters (or something along those lines) of every CanvasRenderingContext2D method being unique, because it compressed code by renaming the canvas methods with what was then a perfect hash function. A new Firefox version was released months later that added new methods and broke the code. That was deemed an acceptable piece of breakage, and Rust's policies are very similar.
Anyway, the vast majority of breakage that we've seen so far was due to the libcpocalypse. This was an issue with people not specifying specific versions of libraries they depended on, not the language.
https://www.fxsitecompat.com/en-US/docs/2015/string-prototyp...
Unless you mean to refute the idea that JS doesn't break anything ever - which it clearly does, occasionally, but proof-of-concept golf code that intentionally takes fragile shortcuts isn't the best example.
...But JS has moved away from monkey patching builtin objects for good reason, whereas Rust tends to be relatively promiscuous in adding methods to other people's types via trait impls. This isn't bad per se, but I do think it creates bigger compatibility risks.
> Approximately 96% of published crate revisions that build with the 1.0 compiler
> build with the 1.5 compiler.
https://internals.rust-lang.org/t/rust-regressions-2015-year...But even at a rate of significantly less than 4%, there's still trouble. That figure is based on the number of root regressions, right? (If I'm wrong, disregard the rest of this paragraph, but I don't think so...) That is, the number of crates that break on their own accord, rather than those that won't compile because one of their dependencies is broken. If one application depends on dozens of crates (or is just really large), the chances of it breaking somewhere are much higher.
This can be partially mitigated by the crates themselves releasing upgrades, but look at Python 3 - there will eventually be a lot of code out there that's completely unmaintained, especially within the "dark matter" of internal or closed source applications. Also, a fix in the latest version of a crate will probably not be backported to long deprecated major versions of it - which means that if I dust off a crate that hasn't been touched in years and uses such a version, I'd have to either upgrade the dependency or do the backport manually.
And the ancestor post did say five years: last I checked on the lists, there was no real consensus as to when Rust 2.0 should be released, on a scale from "in a year" to "never". Maybe there is more consensus internally, but I'd be surprised if it didn't happen in that long...
edit: It's not an entirely fair comparison, but for what my ideal stability promise for a programming language would look like, one need not look very far - only as far as Mozilla's other programming language.
> edit: It's not an entirely fair comparison, but for what my ideal stability promise for a programming language would look like, one need not look very far - only as far as Mozilla's other programming language.
As a matter of fact, JS's stability promise is very similar. See my other post for an example in which JS was broken due to, essentially, one of the reasons you cited, and we lived with it. The JS stability promise is not "we will never break any JavaScript in theory with new browser versions", because that would prevent anyone from, as an extreme example, changing the sorting algorithm for Array.sort. It's "we will not break you in practice, except in a carefully delineated set of circumstances." (Actually, JS's stability promise is much weaker than that: its stability in practice is decided by browser consensus and market forces rather than a clear set of rules.)
The core team feelings on a 2.0 is much closer to the "never" end of the spectrum than the "in a year" side.
edit: Once you google for dropbox rust, you find some really interesting stuff; here's an overview
Putting Rust into production at Dropbox: https://www.reddit.com/r/rust/comments/3wrgl0/what_are_you_f...
Some preliminary feedback on using Rust at Dropbox: https://www.reddit.com/r/programming/comments/3w8dgn/announc...
I'm myself willing to accept the promise of code stability, so I think you're looking at slightly the wrong problem. While I'd expect current code to continue to compile, I also expect what is regarded as good Rust now won't be in five years. That's not a bad thing, but it also means that hopping on the Rust bandwagon might be premature.
The worst case? How about Haskell, where new language features make old code look worse even as it still works? Or C++, with it's accelerating rate of style changes?
Can't speak about Haskell, however I believe that C++ changes can be incrementally adopted: we had C++98, then boost established itself, some parts of boost were included in TR1 and then were part of C++11. A similar process is taking place with other libraries such as asio and filesystem. I don't have the feeling that modern C++ style is in flux, its pillars continue to be RAII, generic programming and the STL.
I think Bjarne & co have explicitly stated that they want to grow the language while maintaining backwards compatibility as much as possible and from my experience, the code I write today builds on patterns and idioms that started to crystalize a long time ago.
Good catch.
Even if it remains a small niche, I think we've seen that Rust is popular enough that using it long-term is fairly safe. Maybe you'll be one of only a few thousand shops who use it -- that's enough. The benefits of writing secure code vastly outweigh to drawbacks of using something unpopular.
When choosing a language to learn, I consider:
* what a particular industry or project of significance is using
* job availability
* compatibility with my existing toolbox
* the power/fun factor
But that's far from the whole story, because programming languages are relatively fungible and investing in domain knowledge, people skills, architecture and many other things will bring bigger benefits than learning that brand new programming language!
And yet here we are, discussing how Rust will deliver us.
> * job availability
That just boils down to popularity once again. I prefer to pick languages based on suitability for the problem domain. For me, Rust is suitable in a way that C++ is not, due to memory safety among many other reasons.
Job availability is not just about number of jobs, but also about project quality, companies and personal preference. E.g: I don't have an interest in containers, and most Go jobs involve containers, so this is a minus for Go.
This works in the other direction too. Let's say I want to work for Mozilla, I notice they use mostly C++ & Js, so I decide to invest time brushing up my skills in those areas.
I agree with that regarding learning them, but I disagree strongly when it comes to the end result.
I make a lot of my money fixing/replacing PHP projects. As has been pointed out many times before, there are issues with PHP that are guaranteed to cause bugs. The same is true of many languages.
But there is a spectrum! Some compilers/interpreters scream at you and terminate if you contradict yourself. Some of them even recognize security vulnerabilities.
I agree that people skills, domain knowledge, and architecture are hugely important, but great teams are still less effective when they use slow or confusing tools.
You might be the greatest contractor in the world, but if someone asks you to build a skyscraper with plywood and super glue, you're going to end up with something dangerous, fragile, and guaranteed to fall apart as soon as someone tries to use it.
The other narrative is also true. Using newer technologies is the best way to be a part of new projects and avoid working legacy maintenance kind of projects.
So why are you here, debating about Rust, if you already know your time would be better spent investing in domain knowledge, people skills, or architecture?
I am not really discussing Rust per se, as I am not familiar enough with it, however, when I see biased or misleading statements I do sometimes feel obliged to offer a counterpoint.
You'd have a similar problem with Rust, Ruby, Python, Java, Ada, Julia, and probably tons of other languages. It must also be really difficult to differentiate between C, C#, and C++, considering most search algorithms will ignore the # and + characters.
1. https://groups.google.com/d/msg/golang-nuts/TtPzOvhG6bM/-XCr...
> I would be pleasantly surprised to see code written today still compile
> in five years.
That's the goal, modulo soundness fixes. We don't currently have any plans for a 2.0. > what will happen to Rust if Mozilla changes strategies
Well, we're just starting the process of integrating Rust code into Firefox. Yes, Mozilla has killed off a lot of things, but I don't think Firefox is going away any time soon.Without changes to the code? Has there even been a language where that has been true?
For instance, here's the compatibility guide to Java 8: http://www.oracle.com/technetwork/java/javase/8-compatibilit...
There a few other little breaking changes as well.
Java stole it's crown for "enterprise" programming and Microsoft even pretty much abandoned it in favor of .NET. I'm not sure their massive push for "C++ is back!" that happened 'round C++11 has really met with that much success - the world moved on, honestly.
There's fewer and fewer niches for it - the web could care less for it, heavy-duty "enterprise" programmers aren't giving up their safe and GC-based Java/.NET languages and giant 3rd-party eco-systems, and even video games now can be written in a higher-level language where only a small core 99% of people never interact with is in C/C++.
The problem is, IMO, that it's too late for C++ to be a decent language. If the first iteration of it was C++11, then sure. But there are vast code bases out there written in the horrible mid-90's or late 80's dialect of C++ that aren't going anywhere. Even something like Qt isn't true "modern" C++, and don't get me started on MFC. Look at Google's official C++ guidelines - they barely allow any modern C++ into their code-base.
> Languages come and go but C++ remains. I wonder if this one will be different.
As I said the other day, unfortunately, the only way to get an old language is to start with a new one, and then let time pass.You're not _wrong_, exactly, but with this logic, no new programming languages should ever be made. There are certainly valuable things about using a truly mature ecosystem, but we also need to build better mature ecosystems.
Well I mean after LISP it was all down hill. /s
I'm not trying to be a jerk. Just voicing my concerns. Learning a new language and producing code in it is a huge commitment. It better be around in 50 years. Will it still be as clean and simple, or will it have grown complex.
Note: That the C++ and COBOL counterpoints look identical and laughable never gets old for me.
Taking the comparison seriously though for a moment. In my opinion the difference is that C++ isn't just a better Cobol. Its not just the same thing re-imagined or cleaned up. There is a revolution between them. You can do things in C++ that you cant in Cobol.
Im waiting for that revolutionary new general purpose language that goes mainstream. I think it will be a language that scales much better and easier to hundreds of cores. Maybe it will be a functional language.
It's a new meme of mine. Glad you had fun with it. :)
"Im waiting for that revolutionary new general purpose language that goes mainstream. I think it will be a language that scales much better and easier to hundreds of cores. Maybe it will be a functional language."
I am too. I think Rust is a nice alternative to C++ for doing that sort of thing. Far as next step, I'm waiting too. I encourage you to check out Julia because it seems to have some of those traits. It's a LISP on the inside with a productive, imperative skin on the outside. Lots of potential for such a hybrid.
Also, check out ParaSail if you're interested in languages designed for easy parallelism. It's an Ada superset with some interesting design decisions. Might be worth factoring into the next, ideal language. ;)
C++ isn't going to go away any time soon, but it might gradually fade into the background.
Are we? I've been hoping but this wasn't on the last C++17 meeting in Kona, Hawaii
https://www.reddit.com/r/cpp/comments/3q4agc/c17_progress_up...
Around 1994 C++ seemed the path forward from Turbo Pascal, given that I favoured type safety, C was a meh when compared with Turbo Pascal, but Turbo Pascal was a PC only thing.
Back in those days C++ was regarded like Rust and other C++ wannabe replacements are nowadays.
We were the hipster of the 90's, with C devs targeting home systems slowly accepting that not all functions needed to be inline Assembly wrappers.
So it is interesting for a grey beard like me to see C++ being described just like C and Pascal vs C++ compilers were back in the mid-90's.
Nowadays my area of work is dominated by JVM, .NET and the native languages of mobile OSes.
I wish Bjarne et al succeed pushing "C++ Core Guidelines" forward, but they will not change the mentality of those that program C with C++ compiler, which is what I usually see at the typical corporations.
10 years is a long time; a few weeks to learn a language is not.
> no autoconf
Mentioning this in the same breath as "no segfaults" seems odd, does Rust use some other build configuration tool, or does it just not have the need for compile-time configuration detection? If it's the latter how does that even work?Are there no optional libraries? No outdated libraries whose version you have to detect? No obscure platforms whose architecture you have to detect for unsafe inline code etc.?
Because if you can have any of those or any number of other cases where you need compile-time detection of configuration options you're going to need something like autoconf.
Yes. Rust ships with cargo, which is probably the best build configuration tool ever written, and I've tried a lot of them, for a lot of different languages.
It has clean, non-hacky solutions to all of the cases you listed, and a lot more besides those.
> happily downloaded a random pre-built ELF binary to build itself.
Yes, Cargo builds itself with Cargo, so you need to get a previous Cargo. The binary is very much not 'random'.The main problem is you can't download dependencies separately from compiling your code. It should never be the same step. Even pip had it mostly correct, by providing an option to disable network communication.
It is somewhat mitigated by documentation mentioning where the downloaded dependencies are put, so you can do all that manually. This sucks balls heavily.
> Yes, Cargo builds itself with Cargo, so you need to get a previous Cargo. The binary is very much not 'random'.
It's fetched outside of regular downloading you do on tarball. You have no control over where is it downloaded from. It means it's pretty much random for your purposes. Heck, it would be even unexpected, if I wasn't used to developers not understanding the difference between building and downloading software.
But this is not the main point. Cargo should not need already compiled Cargo to build itself. This is ridiculous. I understand that every good compiler is built with the very same compiler you're building, but look at how they do that: by bootstrapping. Not so much with Cargo. Who ever thought it as a valid idea?
> It should never be the same step.
This is just going to be a preference then. I much prefer not having to type two things to do what's conceptually one: please build my code."cargo fetch" means you don't have to do the download step manually. IE, you _can_ do it in two commands if you'd like.
> It means it's pretty much random for your purposes.
We are just going to have to agree to disagree on this one. First of all, you _already_ need an equally 'random' binary: rustc itself. Which is also bootstrapped. Second, it's not clear to me why bootstrapping is valid for a compiler, but not for any other kind of project. Cargo is a build system, written in Rust, so it uses Rust's build system.- corporate source code control systems
- corporate central repositories
- caching/proxying immutable repositories
These ensure all projects are built from known sources. We _know_ we can get consistent builds.
When using Cargo:
- Project owners update projects and don't bump the version. New bugs / security problems could be injected even though we haven't changed a thing internally.
- crates.io isn't always up.
- Trust: we legally can not trust (PCI compliance violations - 2015 rules (viral) ) the public crates.io repository. Besides PCI compliance, it's not possible for crates.io to guarantee perfect security (so many reasons, obviously).
* I'm hoping folks who have addressed this issue (or are addressing it, or are planning on addressing it) would comment.
No, it is not.
* It's not always that you have internet connection when building your code.
* It's not always that you have access to the website code is downloaded from (websites break, and companies have different policies about internet), and even if you do, you don't always have it direct (e.g. proxy).
* And at the last, people will build code for various purposes, including building packages. Building a package should be a repeatable operation and should always use only clean source (no artifacts from previous builds), and downloading random things on every build from clean source is very easy way to break the process.
>> It means it's pretty much random for your purposes.
> We are just going to have to agree to disagree on this one. First of all, you _already_ need an equally 'random' binary: rustc itself.
No. I downloaded Rust compiler myself (or had it installed from a package). I controlled that, it didn't hit me in my face unexpectedly.
> Which is also bootstrapped.
s/also//. Cargo is not bootstrapped, because it needs pre-built Cargo to build itself. Compare this to Rust compiler: you start with nothing but C(++? I don't remember) compiler, and end with Rust compiler. No intermediate download involved.
> Second, it's not clear to me why bootstrapping is valid for a compiler, but not for any other kind of project. Cargo is a build system, written in Rust, so it uses Rust's build system.
It's not invalid, quite the contrary. It's just Cargo doesn't boostrap itself out of clean code. It's that simple. I wouldn't have as big problem with it if its build process produced an intermediate, crippled Cargo binary.
I would then complain about requiring external dependencies to be downloaded (in contrast to included), but that would be difference on strategy preferences.
We will see what happens when a vendor write their own Rust compiler that has its own idiosyncrasies.
Compiling pure rust relies on the single compiler toolchain which is assumed to be present by cargo, and universal on all platforms. That's pretty solid.
...but rust = no autoconf is somewhat misleading; rust that uses c libraries has to solve all the same problems that anything else using c libraries has to; but unlike python or node, there is no help (ie. gyp or distutils) for you if you need to do this.
Perhaps eventually something clever will come along to fill the gap, but it's something of a pain point at the moment, especially on windows.
For example:
cfg_if! {
if #[cfg(windows)] {
mod windows;
pub use windows::*;
} else {
mod unix;
pub use unix::*;
}
}
The 13 struct stat's in libc: https://github.com/rust-lang-nursery/libc/search?utf8=%E2%9C...Cargo supports versioning libraries (and seemingly does so sanely!). Obscure platform support is obscure.
However I enjoy even more memory safe system programming languages.
What I love in Rust, besides its ML influence, is making younger generations aware that systems programming languages don't have necessarily to mean unsafe by default or the primitive toolchains from C and C++.
You cannot take C out of C++, so how much help is C++17 if some cowboys in the team keep doing C style coding?
Yes, I know all about static analyzers and code reviews, but the sad truth is that in many companies there are lots of developers and management, that don't care a second about them.
This is specially true across companies whose main product isn't software development.
Const-correctness, use of references ("those things with ampersands by them...." said colleague A), exceptions are all unknown to them.
Object-slicing is a risk ("what's object slicing??").
Pointers are thrown around, added to multiple lists, no RAII, no initialisation of variables, use of memcpy everywhere, no copy constructors, C-style casts EVERYWHERE (including casting to siblings and their descendants...). Arrays are used everywhere, occasionally vectors are used but used like arrays (which isn't so bad), but they're used alongside arrays instead of replacing them; naked new/delete, use of malloc and realloc periodically; for loops are using instead of STL algorithms.
And this is a NEW codebase.
I am desperate to leave.
Does anyone need a C++ developer? Please, someone help!
Just in case it's taken the wrong way - I don't know what the author meant, but gdb works very well with Rust. Actually it "just works" for the debug builds.
There's an old adage: there is no portable software, only software that has been ported. What is the status of Rust on Windows? I understand there are ARM versions; are there any other supported architectures?
I like Rust a lot. It's my go-to systems language at the moment. But let's do keep the hype train somewhere near the tracks.
So, it's definitely achievable. Was done by Wirth at least twice (Pascal/P & Lilith) plus System/38 (later AS/400) at microcode level. Just takes modularity and a good intermediate language. Your adage is still true in that most screw up portability by using ineffective practices or not using effective ones. I rarely believe it when I see the word in a feature list.
No...gdb.
Enjoyed all this this in Haskell for almost 18 years now. 10 years for sure, I don't remember when --make switch in ghc was introduced.It means no need to explicitly define the type of a variable, the compiler infer it based on context.
An algebraic data type is generally made up of two things: 1) structs or tuples, which are useful but in most (but not all languages); C/C++ has them for example; and 2) tagged or disjoint unions, which are different from C unions in that you can tell which one of the union choices is being used (A variant is a particular tagged union).
Tagged unions are super-useful for pattern matching, so you get code like:
match rv {
Ok(_) => {
None
}
Err(CacheError::KeyNotFound) => {
Some(Resp::NotFound)
}
Err(ref err) => Some(from_cache_err(err))
}
which has a nice syntax, is type-safe and can be compiled reasonably efficiently.The algebraic part means that these two features can be used in any combination with each other, and also refers to the link that, roughly 0 is the empty type, 1 is the unit type, structs correspond to multiplies (and gets called the product type), and unions to plus (so is called a sum type).
Why? Because if I have a tuple of a union of A or B or C, and another union of X or Y, I have 6 possible values in total, and (1+1+1) x (1+1) = 6.
Also, in any ADT system 0 should be exactly the empty sum and 1 exactly the empty product. This is what makes things hang together properly.
E.g., this is true in Haskell and it causes there to be some "extra structure" which often has to be smoothed over. Lots of library functions exist just to do this smoothing.
Abstract Data Type were introduced by languages like CLU, Mesa and Modula-2 among others.
It means having modules that only export the type names, but not their definition, while exposing functions/procedures to operate in such types.
It is not the same as the ADTs in functional languages, although they are quite similar.
They being similar how having a limited data structure (non extensible) with a set of functions that operate on it. Depending on the FP language ADTs can also partially hide their implementation like the Abstract Data Types in modular languages.
But I can also happily discuss the math theory and the denotational semantics about them across languages, but I don't think it will help for many readers.
Anyway, my original comment was wrong: in ML, every time you project a type component from a module, you always get the same type. So it's more like a strong existential. My bad.
(0) Rust doesn't really have much support for abstract data types. What Rust has is something not too unlike Haskell: you can hide `struct` fields [though not `enum` constructors], but the `struct` remains concrete. With a bona fide abstract data type mechanism, as in ML, you can give a module a signature that reveals the existence, but not the representation, of its type components. I'm guessing this much representation hiding isn't fully compatible with Rust's goals, which require knowing the sizes of types statically, unless they're behind a layer of indirection.
(1) Abstract data types have no relation whatsoever to type inference. [Nor do algebraic data types for that matter.] An abstract data type is an existential type: a dependent pair consisting of a type `T`, and a value whose type is `F T`, for some type function `F`. The closest thing Rust has to existentials is trait objects, but these can't do everything that existentials can do.
However, there is one serious problem with Cargo's packages: They simply aren't authenticated. Cargo's developers acknowledge this issue and there is a proposal to amend it by adopting the TUF [1]. but this issue doesn't seem to enjoy the attention it deserves.
As a person living in a country that increasingly edging towards going the Kazakhstan route where all secure traffic will be MITMed, simply relying on transport security as a way to secure crates is not sufficient. and that's ignoring compromises due to server security failures. At present, my only options are to include the entire dependency tree in crate or avoid dependencies altogether.
Even ignoring my problem (which wouldn't effect a large number of rust developers), Mixing transport security with authentication and integrity shouldn't happen in vital infrastructure like a language package system.
There are other problems like not representing system libraries that a crate depends on in the project manifest, but unauthenticated crates are a ar more serious problem.
+ Compiler with very helpful error messages;
+ Cargo is the best package manager in the universe;
+ Result<> is the most correct way to return results from function, I dreamed about it;
+ Community of smart and friendly people.
Minuses:
- Functions are not first class citizens - you can't return function as result;
- Violation of "explicit over implicit" rule in methods declarations: argument self/&self is implicitly omitted in call, but exist in signature. Because of this, for example, you can't use method of class as request handler in Iron, you need closure wrapper with same order and count of arguments. Pure ugliness;
- Often you need just add lifetime definition to satisfy compiler, without doing anything with this lifetime anywhere else in code;
- Extremely verbose syntax;
- Rust has exceptions (panic), and you can't catch them. And with promised panic::recover you'll have limitations of supported structures;
- Errors handling encourages copy-pasting;
- Syntax is not intuitive, especially when traits and lifetimes comes into declaration - in such cases you can look at own code and think "I hope at least compiler can read it";
- Nested structures is pain;
- Lack of optional and named arguments, lack of predefined values in structures;
- Too few modules for web. Just 1 module for MySQL (thanks to it's author - actively maintained);
- Usage of safe code can lead to out-of-bounds errors, with thread panic (hello to vec macros).
And I still think it's best language we have:) Biggest advantage - most of errors can be found on compilation step. But price is all of these minuses.
Of course you can: http://is.gd/YASF8T
One issue is that closures don't really have their own types, so to return closures you need to use trait objects, which means dynamically-sized types, which means you have to box them
- Rust book on the subject: https://doc.rust-lang.org/book/closures.html#returning-closu...
- Example of returning a boxed closure: http://is.gd/pJIqiu
> Violation of "explicit over implicit" rule in methods declarations: argument self/&self is implicitly omitted in call, but exist in signature.
That's a debatable assertion, in a method call, the self/&self is the subject, the part before the dot.
> Because of this, for example, you can't use method of class as request handler in Iron, you need closure wrapper with same order and count of arguments.
By "class" what do you mean? Because AFAIK it works on struct methods http://is.gd/xu6HRc
> Rust has exceptions (panic), and you can't catch them
That's because Rust doesn't have exceptions, it has panics, they're not a general-purpose error-reporting or control-flow mechanism.
> lack of predefined values in structures
http://doc.rust-lang.org/std/default/trait.Default.html
> Usage of safe code can lead to out-of-bounds errors, with thread panic (hello to vec macros).
Safe rust doesn't mean no panics. `panic!` is safe rust.
I don't see how you can declare field if structure to have default value 65535, even with Default trait.
I posted opinion not to argue and really frustrating to see downvotes for opinion. Make discussion more constructive.
println is not relevant here it only shows the result of the call, the point is that you can use a method as a function, I expect the issue is elsewhere (but not having used Iron, I wouldn't know where).
> I don't see how you can declare field if structure to have default value 65535, even with Default trait.
Implement Default then call it when instantiating struct: http://is.gd/2YSoKd
> I posted opinion not to argue and really frustrating to see downvotes for opinion. Make discussion more constructive.
Er… I didn't downvote you? I just replied to some of your objections which I assume is what you expected?
It's far from "default values in struct declaration". I mean this:
struct X { i:u32 = 5, j:u64 }
And still: no. When signature is declared strictly (and in reality it's all cases except println and format), you can pass only function with matching signature. self/&self changes signature.
Oh, that's another minus: love to redefine well-known terms. Panics are exceptions, they behave exactly as exceptions, just unhandled. All other is just casuistry
The point of calling them panic is so they're not interpreted as exceptions and people used to exceptions-as-an-error-reporting-mechanism don't try to use them so.
> they behave exactly as exceptions, just unhandled
The "unhandled" part is exactly why they're not called exceptions.
Catching panics should never be part of an application's normal control flow in Rust, unlike e.g. Java and C#. These languages use exceptions to indicate any error condition, and therefore writing correct error handling code requires try/catch.
Exceptions in Rust are used as a "soft abort". A real abort tears down the entire process, and the OS handles releasing all associated resources. But if you're writing a program that has logical tasks that resemble processes, the OS can't individually tear those down for you. When an exception (panic) is thrown in Rust, the task has hit a situation that cannot be recovered, and tears itself down so the rest of the application can reclaim the resources.
The places in Rust where exceptions are thrown are usually places where the programmer has chosen not to handle a logic error -- usually indexing out of bounds, integer overflows (sometimes), or forcing an optional value. Things that would be RuntimeExceptions (unchecked exceptions) in Java.
> Violation of "explicit over implicit" rule in methods declarations: argument self/&self is implicitly omitted in call, but exist in signature. Because of this, for example, you can't use method of class as request handler in Iron, you need closure wrapper with same order and count of arguments. Pure ugliness;
You can call methods with explicit self too.
In this case you would pass `Foo::bar` down to iron. Assuming the arguments are in the right order.
> and you can't catch them
You're not supposed to. Panics are to be used as less as possible; only for irrecoverable things or for application-level panicking.
> Nested structures is pain;
Could you expand on this point?
> Usage of safe code can lead to out-of-bounds errors, with thread panic (hello to vec macros).
It's idiomatic to use iterators as much as possible, or use the monadic indexing methods (which return Option).
Point is "signatures are different". You can't use this function in place where you need signature without &self.
> You're not supposed to
It makes life of web-servers harder.
> Could you expand on this point?
To parse JSON with nested objects you need to declare a lot of structures. When APIs are difficult, people even use code generators.
> It's idiomatic
It's not an argument. What work should work. Idiomatic or not - another question.
The signature is fn(&Foo, u8). If that's not in the right order, you can always put it in a closure -- this is not a Rust-specific problem.
> It makes life of web-servers harder.
I don't see why web servers need exceptions. You do have catch_panic if you want to prevent the application from ever crashing; that's what it's for (rewrapping fatal errors at the top level or preventing panics from leaking through FFI)
> To parse JSON with nested objects you need to declare a lot of structures.
Fair enough. I think there was a nested hashmap based JSON library somewhere?
That's where I started. And it's Rust-specific problem because of implicit "self" argument, needed to send link to instance.
This is no different from having a function which takes parameters in a different order from what's expected, and is not specific to Rust.
"implicit first argument" can be made explicit, we've established that. If the signature is still different that's an issue with order of arguments.
If you don't want it to take self at all, well, it's a method, that's how methods work, they have a receiver. In all languages.
(Personally I use Scala, but using Maven for dependency management rather than SBT. It doesn't tick all your boxes (in particular I'm thinking of one specific member of the "community") but it's close, and deals with a lot of your minuses)
I don't see much of an alternative to panic on bounds checked indexing error, short of not providing indexing at all or developing a system of proven indices that would be extremely unfriendly to newcomers, see this project: https://github.com/bluss/indexing
Returning an Option. That's what Elm does:
head : List a -> Maybe a
get : Int -> Array a -> Maybe a
It tends to drive people towards not indexing collection (which is often good).Perhaps we should have a clippy lint for this.
I have seen quite a bit of regular `[]` indexing, however it's almost always in very obvious situations, and most of the places where you might index in C/++ (iteration, or with a bounds check) I've seen people using iterators or the monadic way.
But yeah, it's not that unidiomatic. It's still used a lot, just not to the extent it's used elsewhere.
(For example, we have thrice as much indexing in our DOM binding generation python code (`components/script/dom/bindings/codegen`) in Servo than we do in all of the non-test rust files (`components/`) in the main repo combined)
There may be ways to lint in case of an `if x.len()... x[...]` though. That should be done monadically; given that panics aren't supposed to be caught in most situations, we should minimize the number of ways programmer error can lead to a panic.
Looking through servo's code a lot of the cases are where we use fixed-length arrays, or when dealing with graphicsy buffers. One use case which needs indexing is when we use an index as an "id". No easy way to fix that without introducing Rc or something.
struct Foo;
impl Foo {
fn bar(&self) {}
}
fn main() {
let x = Foo;
Foo::bar(&x);
}
What you may be encountering is you can't pass a `Foo` when a `&Foo` is expected (why I needed to write &x), because the types differ. Calls with method syntax don't see this because they automatically borrow the receiver if necessary, but explicit function calls do not do this sort of borrowing and so the types need to line up right.Deleted comment
Maybe that's ugly, but it's explicit. The method is statically compiled into the program to take the receiver as an argument. What you want to do is specify the receiver argument on the caller cide and the remaining arguments on the callee side, which is impossible to do. The closest you can get is to create a closure on the caller side and pass that to the callee. Languages that avoid this, like say, python, are just implicitly creating that closure. Creating a closure implicitly goes against the design goals of rust.
At least rust doesn't have javascript's abominable semantics.
Secondly, Rust's compiler gives INCREDIBLY detailed explanations of what the borrow checker doesn't like, where the conflicting scopes begin and end, and helpful suggestions of what to do to make it work.
I think this is the difference when coming from a primarily GC'd (So I suppose "non systems") language. Coming from C# I never even thought about the difference between storing a concrete type in an array vs storing an abstract type or interface in an array. In C# so long as they are reference types, there is very little difference. Each entry would be a pointer to the heap regardless. Moving to a language that encourages stack allocation and structs on the stack instead of on the heap, and actually has you motivate each pointer is a huge difference.
The same goes for ownership: it's very easy to forget it with a GC'd runtime (this is just sloppy, but the GC will unfortunately help you be sloppy). Way too often the code you write in a GC'd language will have convenient pointers both up, down and sideways in any data structure, just in case. Writing a doubly linked list in C# is no harder than writing a singly linked list. In Rust the singly linked list is lesson 1, and the doubly linked one is much later.
I'm sure I'll get it with time (if I don't give up first), but often I just don't understand what the error messages or the referenced help mean.
A big problem for me is that the language syntax alone doesn't map to valid code. You edit your code removing the compiler errors until it is syntactically correct, then the borrow checker kicks in and tells you that the whole thing is wrong. So you end up deleting a whole module and starting again. Errrh!
Also - with all the changes to the languages, most of the answers on stackoverflow should be tagged as out-of-date.
Looking from the perspective of implementation of those algorithms, it is no longer that simple and single iterator is rarely sufficient, consider: std::unique, std::reverse, std::partition, std::sort, std::inplace_merge to name a few, where there is much more to it than just checking for end.
https://doc.rust-lang.org/std/iter/trait.Iterator.html
But some of the more specialized ones don't work using just iter. But on the other hand, no iterator invalidation.
Maybe it would be possible to implement those algorithms in Rust in terms of ranges like in D, instead of iterators? I will have to try and see.
Ultimately though, there just hasn't been a lot of demand for ranges. Iterators and slices do most of the work people care about. We actually had tried to make iterators more like ranges back in the day, but we tore it out because no one cared. To this day you can't sort a VecDeque, and no one has ever bothered us about it.
It seems to me that you can go quite far with mutable but mutually disjoint ranges - which is more or less what slices do for vectors currently. They are also safe, because you can only split them into subslices (which borrow ownership), but not extend them (what could potentially create overlapping ranges). Moreover as long as there is any range to given container, you can't perform operations that could invalidate derived ranges (things that reallocate vector, etc.)
Range checking don't seem that bad, because in most cases it is not about trusting what a range tells you, but range trusting itself which is fine. Take you example of binary search, if range would know how to split itself in the middle, it wouldn't really have to do any bounds checking. For sorted ranges maybe operation like lower_bound or upper bound would be great primitives, or single operation that encompasses both situations:
let (lower_range, equivalent_range, upper_range) = range.split(&value);
Of course, in general as you point out, you would have to pay a price of bounds
checking for random access.As far as I can see, this should be sufficient to implement algorithmic part. What I still find hard to do, is to modify collections based on resulting range. For example, how to write equivalent to following C++ code, that first moves consecutive duplicates to the end of array, and then erases them from container:
std::vector<int> v { ... };
v.erase(unique(v.begin(), v.end()),
v.end());
I have a few ideas, which mostly boil down to following: create a description
of operation to be executed, but is executed only after all ranges have
returned their ownership over collection. But so far this interface have not
been fully satisfactory.(Rust has Cell/RefCell if you need this, though)
x.remove(x.front());
Comment for non-C++ programmers: front method returns a reference to the first
value in the list, and remove takes a const reference to value and removes all
elements that compare equal to it. Problem is of course that after comparing
first element with provided argument, it would compare equal and be
subsequently deleted, making the reference invalid. As a side comment, I will
note that above is in fact required to work, though it is probably not something
you should write.A gcc front-end would be great, if only to show that the language spec is mature and complete enough that other implementations can be built on it.
rustc already supports lto (lto=true in cargo manifest, or -C lto in rustc)
> all the list of target backends GCC support.
Which targets are you interested in specifically?
Is the author saying Rust is a breath of fresh air from C++ or from all other programming languages? Ruby was my breath of fresh air for a while, but lately I've not been enthusiastic about any language or framework, really, so I curious how great Rust really is these days.
"In Python I try to avoid even having dependencies if I can, and only use the standard library. I don't want my users to have to deal with virtualenv and pip if they don't have to (especially if they're not pythonistas)"
This rings so true to me. Package management in Rust is much better than in Python.
Also, Rust feels a bit like "pythonic systems programming". I mean that it just feels like the right tool for the job. And it might feel a bit verbose in the beginning, but in reality it's explicit and precise. It feels pretty ergonomic just like Python.
Isn't it fantastic that people start comparing Rust to more high level languages like Python and Ruby instead of C++ or C? It says a lot about the language design and its goals.
"pip install -Ur requirements.txt" and you're done. A virtualenv is one extra line.
Python's dependency management is great for devs who have a GCC toolchain, but it really sucks when you need to share code with non-developers.
That's sad... There are so many languages out there, and not even one seems interesting or fascinating to you? Take a look at my list[1], maybe you'll find something you'd like ;-)
Anyway, Rust and Python are too dissimilar to compare them and decide which is more of a "breath of fresh air". It would be more meaningful to compare Rust with C++ and D. I wish someone did a blog post about it.
fn something() {
foobar();
{
bar();
foo();
}
quux();
}
That is much easier to understand, and easier for tools to work with (they can actually automatically indent your code without breaking it), than: def something():
foobar()
bar()
foo()
quux()
edit: And how would you even add a scope right after another block (like if)? That would require you to have a keyword anyway.Whitespace / indentation used for scope delimiting in Python is a bad idea. No, thanks. I'm glad Rust isn't using anything like that.
use std::collections::{HashMap, HashSet};
Are all those ::s and {}s adding to clarity?Why are there no easy to use initializers for maps? It's 2015! I have to do:
let mut map = ::std::collections::HashMap::new();
map.insert("a".to_string(), "b".to_string());
...
or some such nonsense. Oh, right, I have to write a macro and basically learn another language. Also, using the macro system for basic things like println just seems bonkers to me; use macros when you really, really need to do something special, not everywhere.[1]: https://en.wikipedia.org/wiki/C%2B%2B11#Initializer_lists
I.e. I'd prefer to write:
let v: Vec<i32> = {1, 2, 3, 4, 5};
Instead of let v = vec![1, 2, 3, 4, 5];That said, in future, it is likely to be possible for things like `Vec::from_array([1, 2, 3, 4])` and `HashMap::from_array([[1, 2], [3, 4]])` to work.
Exact implementation is another question. That's why asked, whether it's feasible or it's something hard to fit into the language.
I'm not sure I personally feel that the first is particularly better than the latter, but it should have to be by a large margin to justify adding new syntax to the language.
> it should have to be by a large margin to justify adding new syntax to the language.
C++11 put some effort into simplifying some cumbersome syntax constructs. It makes things easier to read and write which pays off.
Interfacing Rust and python, ruby, node can be quite simple in fact (micro demo for node: http://calculist.org/blog/2015/12/23/neon-node-rust/)
See this from the "D" documentation: "Being 100% compatible with C++ means more or less adding a fully functional C++ compiler front end"
C code, works fine, as always.
What really bugs me, is what the author describes as being'concise'. Shortcuts like fn, pub or vec, in my oppinion, hurt readability more than they help me to write code faster. But maybe thats just my brain, or lack of time using rust.
http://stackoverflow.com/questions/27535289/correct-way-to-r...
https://www.reddit.com/r/rust/comments/2h26cj/functions_retu...
https://github.com/rust-lang/rfcs/pull/105
https://github.com/rust-lang/rfcs/pull/1305
Reading those RFCs can be frustrating, where a discussion goes on for over a year and people add more and more wants to an idea until it sinks. Same thing with the discussion about a `yield` keyword:
https://github.com/rust-lang/rust/issues/7746
https://github.com/rust-lang/rfcs/issues/388
It reminds me of the JCP in Java. I guess every standards committee is like this, and I actually like how careful the Rust team is about extending the language---but I think we should be prepared for it to evolve slowly. Right now I feel like Rust's pace will be more like Java than C#.
Also, this particular feature seems easy from a usage perspective, but the type theory behind it is actually pretty intense, and there's multiple possible ways of doing it. So those need to be explored, discussed, and figured out.
This feature is one that's of high importance, and is coming sooner rather than later.
Also thanks to you personally for your work making Rust accessible to us newbies. The book is very nice, and a few weeks ago you were really quick to answer a question of mine on IRC. Keep up the good work!
You're welcome. I hope the next iteration of the book is even better :)
Thanks for the book, by the way! It's an awesome resource. :)
Here are the two RFCs: https://github.com/rust-lang/rfcs/blob/master/text/1191-hir.... https://github.com/rust-lang/rfcs/blob/master/text/1211-mir....
And thanks! I'm hoping the second draft will be even better.
Note that there is good activity around abstract return types happening (seems to be something the core team is putting effort into?), so it might happen soon.
More interesting to me would be comparing it to a new-ish multi-paradigm language, of which there are many right now (e.g Swift, OCaml, Haskell, F#, Scala). It seems like in practical applications these newer FP languages will be much more productive than Rust, as memory management is much simpler. Rust's principle of zero-cost abstractions is great in theory, but can't think of many applications where it's the limiting factor except perhaps embedded devices and kernel development.
I would consider systems programming to be things like writing operating systems, drivers, high-performance native applications (for many difference definitions of high-performance), and databases.
I consider rust to be a high-level language and I further consider it to be suitable for systems programming using my definitions.
However, I don't consider go to be suitable for a lot of systems programming tasks because it has a significant runtime system that includes a garbage collector. Just because they describe themselves as a "systems programming language" doesn't mean that I believe them (at least as far as I understand that term). That said, there are certain applications that work fine with GC, and in those cases it can be a very nice way to achieve memory safety without programmer burden.
[1] https://en.wikipedia.org/wiki/ML_%28programming_language%29
It's an illusion to think that in a language with a garbage collector you do not have to worry about memory management. To give an example: take a function that takes a substring of a string.
1. If a substring function returns a string with the same underlying character array (just with different start/end indices), you may be wasting memory with very large strings of which you only care about small substrings.
2. If a substring function returns a string with an underlying character array that is a copy of the particular substring, you may waste a lot of memory when you have a lot of (partially) overlapping substrings. Moreover, the substring operation is O(n) rather than O(1).
In other words, in any non-trivial application working with strings you want to know the allocation behaviour of common string operations.
String someLargeString = ...;
//...
String interesting = someLargeString.substring(10, 20);
someLargeString = null;
Now assume that someLargeString is garbage-collected after the last statement. Is the backing character array of interesting 10 chars long or the size of the backing array of someLargeString?You don't know, unless you know what the implementation of String#substring does. Suppose that someLargeString's array was a couple of megabytes, you could be using 20 bytes of memory or megabytes for interesting.
(Note that in the case of Java they switched from a O(1) index-based slicing to O(n) copying some time ago.)
Can you give an example of a (somewhat widely-used) library/runtime that does this?
I think this is harder than it initially seems. How does the GC know that if you have some type with a backing array and two pointers (or indices) that I don't want to move one of the pointers to some other valid position later?
I am not arguing that it is not possible, but this seems difficult in the general case, unless you can explicitly provide hints to the garbage collector.
At some point this for considered for JS engine in Firefox, but that was before introduction of ropes, small strings that holds string chars inline and various other optimizations like recent switch from UTF16 to byte per char for strings with Latin1 characters. That and the fact that Firefox flattens all strings that name properties of objects, this optimization is unnecessary on the current web.
Exactly. Which is the core of "not worrying about it". When it becomes a problem (Such as an out of memory exception) you worry about it. There is no illusion. The time you don't worry about it is when you write it. I'd write it exactly like you did above (except I probably wouldn't null it), and wouldn't think of it again until it became a concrete problem.
You are absolutely right that a GC didn't magically make all memory issues disappear. It just hides them.
But that tradeoff of memory footprint vs performance implemented by Oracle is not in the API documentation[1] for Java 7 nor Java 8.
One would have to stumble across other sources such as unofficial blogs[2] written by non-Oracle employees like Mikhail Vorontsov. That blog article then triggers an explanation from the actual coder that's buried in a reddit thread[3].
Hunting down why your business-domain java code is now suddenly slower because of hidden GC tuning is taking time away from "solving the problem."
Your rebuttal consists of:
-- repeating the "premature optimization" meme which is not relevant in this case.
-- recommending to look at the API docs, which doesn't even have the information!
[1]v7: https://docs.oracle.com/javase/7/docs/api/java/lang/String.h...
v8: https://docs.oracle.com/javase/8/docs/api/java/lang/String.h...
[2]http://java-performance.info/changes-to-string-java-1-7-0_06...
It's not a contrived example. When Java changed this exact behaviour it caused a lot of pain for people. Code that previously ran in a reasonable amount of time suddenly became impractically slow:
https://news.ycombinator.com/item?id=9862556
>may require a quick look at the API docs
The behaviour isn't specified by the spec. That is why they were able to change it from one release to the next.
>premature optimization and all that.
The "premature optimization" meme you are parroting contains the implication that at some point optimisation will be necessary. When that happens you have to think about memory management, even in a GC language. The string example given by danieldk is just one of many ways in which that can happen.
That's exactly the point: caring about the actual memory used by interesting may not be as important as other parts of the application, or may not be important at all for now. You should only care about that when it does prevent pose a problem (performance, impossibility to evolve features, ...) (all while making the initial code flexible enough to be potentially changed in the future)
[1]: At least between GC and not-GC. Rust is a new third option IMHO.
We're not getting more cycles any time soon for sequential programs.
Other peoples problem constraints are not necessarily the same as yours.
Most GC problems can be pinpointed to poor application behavior and many are due to bottlenecks in GC (e.g. too much work done in sequential phases, etc).
I'm talking 10-50x performance increases here. Unfortunately managed languages(and GCs in particular with everything being a ref) make this much harder than it should be.
In this settings GC is not necessary beneficial as it brings an extra unpredictable latency/pauses. All is fine when it does not affect the application, but when it becomes the problem, it is hard to fix and may require a significant rewrite or a very time-consuming tuning of GC parameters.
Even for most normal web apps a full stop the world GC is a common occurrence and a deadly one.
I love C#, and I love not having to write code that worries about memory (99.9% of the time anyway).
But if I was writing the .Net Runtime then I wouldn't be writing that in C#, because the runtime _does_ have to manage its own memory - and Rust looks like a very good language for writing that kind of thing in.
Oberon's GC is written in Oberon:
https://www.inf.ethz.ch/personal/wirth/ProjectOberon/Sources...
Module SYSTEM is like Rust's unsafe {}.
Singularity and Midori GC's were written in their C# dialects.
With luck C# 7 onwards will get all the nice features from Sing# and System C#, as per Joe Duffy's blog.
Then using C# will be no different than Modula-3, in terms of features for systems programming.
There is a huge category of bugs related to sloppy fudge-factor-based programming. C++ has great tooling but it's a nightmare on an organizational scale. If I was building an engineering org and had no other alternative but C++ I would be in shambles worrying that no-one does anything horrible, no matter how good the engineers were.
I write C++ for my day job :)
We can easily fix tools and processes, we will never fix people. Sure, all languages allow to create bugs, but some orders of magnitude more then others.
> kernel is written in C and is the most successful/widely used piece of software
end every release finds bugs in filesystem drivers, and buffer overflows and security issues and breaks display drivers. Seriously think of all the things kernel developers could do if they didn't waste years of work on tracking invalid pointers and off-by-one errors. Over and over and over again.
It allows managing arbitrary resources more easily, so it is clear who is control of things at any point of time, e.g. if a function takes a File, who should close it (if the callee closes it, or if the caller needs to) is just a matter of documentation, while it is expressed naturally in the Rust type signature, and is hence checked by compilers. This isn't unique to Rust, e.g. it's basically the same as C++'s RAII but it is checked far more precisely by the Rust compiler: it is up to the programmer to not make a mistake in C++. http://blog.skylight.io/rust-means-never-having-to-close-a-s...
It is also an important part about getting guarantees for concurrent code, as it allows fairly precise control over sharing (or not). This not only means Rust code without `unsafe` is free from data races, but also means that, for example, one can be sure a program built on message passing (ala CSP/Go) is not accidentally sharing things that shouldn't be shared. http://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.ht...
Writing in a language with proper types helps a lot. Being forced to acknowledge every error returned is great. The only time I had to think about memory management was during integration of database connection pooling and the web framework, but that's just because I was one of the first ones to try. Otherwise memory management doesn't get in the way at all.
My hypothesis was that the type system is be able to benefit practically all domains. Personally, so far, I've become a cautious believer.
> To be clear, a major goal of this project -- outlined in the earlier blog posts -- is "GC as Interoperation Feature". That is, we want to embed Rust code into contexts with existing GCs (such as the V8 or SpiderMonkey Javascript engines), and so Felix is trying to find ways to accommodate a relatively wide range of GC approaches, with maximally safe, efficient, and ergonomic support on the Rust side.
Might be a while off before good support for custom GCs for pure Rust code, but it's definitely being thought about.
[0]: https://github.com/rust-lang/rfcs/pull/1398
[1]: http://blog.pnkfx.org/blog/2015/11/10/gc-and-rust-part-1-spe...
[2]: http://blog.pnkfx.org/blog/2016/01/01/gc-and-rust-part-2-roo...
[3]: https://www.reddit.com/r/rust/comments/3zqtoc/gc_and_rust_pa...
Jai [1], Jonathan Blow's language in progress, has a ton of customizable features. For example, the memory allocator is quite straightforward to swap natively without any hacks.
[1]: https://github.com/BSVino/JaiPrimer/blob/master/JaiPrimer.md
I'm also interested in one particular kind of memory abstraction: abstracted access to memory mapped files. Here the problem is that the base address of the memory mapped area might change whenever you perform the mapping. And you would like to use standard data-structures (like maps, lists, etc.) inside the memory mapped area.
A library that can do that in Rust would be great.
The main problem is not opening the mmapped file. The problem is having a kind of pointer that allows arbitrary offsetting, and using this pointer inside standard data structures.
I can see why you might want to know in the case where you're representing an external resource, e.g. an open file, but otherwise, I don't see any reason to care about it.
In face, with persistent data structures, you can't know. The same is true of share (immutable) data with multiple threads. You can't know the lifetime.
In the space of core engine stuff, I agree.