Hacker News new | past | comments | ask | show | jobs | submit login
State of Rust 0.11.0 (octayn.net)
170 points by cmrx64 on July 16, 2014 | hide | past | web | favorite | 112 comments

That is very exciting. I like the direction the project is going.

But, one thing worries me -- performance. Anyone know a list or tracker of benchmarks to see how it roughly compares.

Or is it too ealry too talk about it? Am I pointing to the elephant in the room? I am ok with saying "we are not there to talk about it, stop stirring the pot". I understand that.

But looking at the future, if Rust is to compete with C++, it needs to perform close to it. Not necessarily in the "safer"/default mode, but it needs to have an unsafe, turn-off-all-checks-run as fast as you can mode.

C++11/C++14 improved C++ many enjoy and like using it. Many that don't are probably using it for performance reasons. That is a niche Rust will have to compete in.

> Not necessarily in the "safer"/default mode, but it needs to have an unsafe, turn-off-all-checks-run as fast as you can mode.

By design, Rust allows you to opt-in to this mode locally via `unsafe { ... }`; this allows you to be 100% safe through the vast majority of the code base, but then eek out the very last drop of performance in core routines where there's no safe alternative. Then only those regions need to be checked for memory safety vulnerabilities rather than the whole codebase.

Although, many things can be written in safe code and still be performant: iterators provide safe and highly efficient sequential access to vectors and arrays (right now they optimise to the same code as the equivalent C/C++, other a bug in LLVM where it's not recognising that some pointers are guaranteed to be nonnull).

> By design, Rust allows you to opt-in to this mode locally via `unsafe { ... }`;

It should be noted that contrary to e.g. Swift's -Ofast, Rust's unsafe {} does not change language semantics. It allows operations marked as unsafe (non-bounds-checked array accesses, raw pointer and pointer munging, etc…) but operations usable outside unsafe {} have the exact same semantics inside.

Rust's performance is competitive with clang and improving, even in the "safer" mode. We have benchmarks in many of the libraries (search for #[bench]) and some in src/test/bench. We're on the benchmarking game, but that is really just a game, not indicative of anything besides how much time someone has spent microoptimizing a specific program. Someone is working on a custom LLVM pass that will remove essentially every null check that Rust adds. On top of that, there are still a few more changes on the way that aren't easy, but should give a nice perf improvement.

Here is a link to the source code of the initial version of the custom LLVM pass to eliminate null checks. Hopefully this will be upstreamed to LLVM in the future.


There's a small typo in there: argumetns -> arguments

> but that is really just a game,

Very much so, agreed. It is a PR game most of all. It is unfortunate. But it is probably a game that needs to be played. I know microbenchmarks are bogus, but I still click and look at them.

>But it is probably a game that needs to be played.

Not really. The language is supposed to catter to professional programmers, not script kiddies.

The games are really awful. Look at NBody for example: http://benchmarksgame.alioth.debian.org/u64/performance.php?.... The fastest C program does it in 10 seconds. Rust is at 21. What's the difference? The C program uses vector intrinsics, while the Rust program is a straightforward implementation. The next four fastest C programs, which don't use vector intrinsics, are only a hair faster than the Rust program (20.x seconds).

The script kiddie's takeaway is: "well, C is still twice as fast as Rust!" The real professional's takeaway is: "wow, Rust manages to get within 4% of C while using iterators and higher order functions in the inner loop! Also: "Rust can do inline assembly too if that's what I need." https://gist.github.com/luqmana/5150557

Hate to break it to you, but most professional programmers have no deep knowledge of:

* runtime systems * detailed memory management * algorithmic complexity

Yet, quite a lot of those build parts of huge systems that work and meet business requirements all the time, which makes them very desirable professionals.

Being able to competently benchmark is a fringe skill.

Sure -- but I'd call them "employed programmers" (as opposed to "professional").

In any case, I don't think Rust's target group doesn't know about runtime systems, detailed memory management and algorithmic complexity.

After all it's mostly if you care about all three that you'll consider Rust.

"professionals" are, by definition, people that are employed in a profession.

Sorry, downvoted you by mistake. Wanted to upvote, because yes, that's the definition of "professional", contrary to an "amateur" which is somebody engaged in a certain activity without monetary compensation (for the love of it only).

Sorry, but "by definition" there are several meanings in the dictionary. Merely employed is the lower level. And I think I made the way I mean it clear.

E.g From dictionary.reference.com:

noun: - a person who is expert at his or her work: You can tell by her comments that this editor is a real professional. - a person who engages in an activity with great competence

adj: - extremely competent in a job, etc - (of a piece of work or anything performed) produced with competence or skill

Yes, and I think I also made clear that I don't accept you using solely that definition to discredit and downplay achievements of people not concentrated on a fringe of things. Your comment is the elitist as hell.

For example, someone writing a huge distributed video processing system rarely needs to consider any of these topics. Because everything needed in that range is built. Making sure that the data is at the right place at the right time and predicting that needs none of the skill given by you. Is that person not a professional?

>Your comment is the elitist as hell.


Expecting professional programmers to be familiar with "runtime systems, detailed memory management and algorithmic complexity" is "elitist as hell"?

That's one of the reasons the industry is lead by charlatans pushing the latest fad, doesn't know it's own history, and has built pile upon pile of cruft.

>For example, someone writing a huge distributed video processing system rarely needs to consider any of these topics. Because everything needed in that range is built.

Blind reliance upon things build by others, the characteristics of which one building the "video processing system" you describe is not able to assess, and is not able to modify to suit the project's specific needs. That we accept that is a lowering of the profession. It's like doctors deciding it's no need to know pathology and anatomy anymore, because there are expert systems they can use.

> it needs to have an unsafe, turn-off-all-checks-run as fast as you can mode

This will never happen, as it is contrary to the goals of the project. As mentioned by others, locally, you can ignore some checks, but there won't be a compiler flag to turn them off.

In some ways, it's too early to talk about it, and in some ways, it's not. It's not too early to talk about decisions which would place a certain upper bound on performance, but it's kinda too early to do some kinds of direct comparisons. The team has not really been working on performance generally, as the focus is to get the language semantics correctly. There's lots of places, for example, where rustc generates subpar LLVM IR, and it's a matter of someone writing a patch.

There technically is an "unsafe" mode, but not in the way you're thinking. You can drop down to C level by writing code in unsafe blocks and using methods prefixed with "unsafe." This will stop the compiler from doing things like inserting bounds checking and should perform fairly close to Clang.

Generally though, rust strives for the same ideal as C++: you only get what you pay for. You can carve out the subsection of the language you need through things like feature gating without too much trouble right now (and the standard library is still improving in this regard).

Just a quick reply to link to this answer on reddit with the question:

Does 'unsafe' disable array bound checking? http://www.reddit.com/r/rust/comments/2aqqxa/does_unsafe_dis...

Oh, the open door to security exploits that C developers love so much.

When Hoare designed Algol(1960) he was explicitly requested not to allow it, due to security concerns[0].

[0] "The 1980 ACM Turing Award Lecture",

>But, one thing worries me -- performance. Anyone know a list or tracker of benchmarks to see how it roughly compares. Or is it too ealry too talk about it? Am I pointing to the elephant in the room?

I'd say it's too early.

First, Rust is a compiled, statically typed languaged using LLVM, including stuff such as non aliasing guarantess over C, and designed to be secure AND fast. It's not like it's some scripting language.

Second, you should benchmark your program first. Worrying about a language's "performance" in abstract is more premature than even Knuth anticipated.

A fast language runtime helps all programs. If it's slow, there's precious little you can do about it. And the design of the language may end up such that certain aspects are just always going to be slow. So, I very much disagree with the notion that this a premature optimization (and I'm starting to think that phrase needs to just go away). You seem to allude to as much by delineating between compiled and scripting languages.

You can run Rust completely without a runtime. Part of the reason for that is that there are much bigger performance hawks than you or me monitoring Rust who have significant input on its implementation. That is one of the reasons that Rust moved GC into a library, for example. The last thing I am worried about with Rust is its performance.

> You can run Rust completely without a runtime

That doesn't say much about performance. Languages that do need a runtime, like Java, aren't slower than C++ because of the virtual machine per se (which otherwise does a good job at doing awesome optimizations that are only possible with runtime profiling), but rather the reliance on a garbage collector and the lack of options for fine tuning the memory layout / access patterns and unfortunately the runtime is not smart enough yet to do that for you. Every language that depends on a GC, but that wants to be close to C/C++ in performance, starts with this handicap, even though for most purposes an efficient garbage collector will behave much better than inefficient usage of malloc or a half-assed object pool.

Of course I wouldn't worry about Rust's performance either.

Awesome. Rust's performance is something that impresses me, so I'm certainly not suggesting that it's slow. The point I was making is it's a valid question to ask and a valid thing to measure. Having fought with slow language features (e.g., blocks in Ruby), slow stdlib (e.g., older versions of Scala's collections), and slow techniques adopted by library authors (e.g., ask for forgiveness with exceptions instead of permission) there's just a lot that impacts many if not all apps and if it's outside your own application code, it can be very hard to fix. One of the things I really like about go releases is they indicate exactly what's changed on the performance front, so I have a good idea of what to expect. And since they measure it, they can improve it.

Sure. I think that until they hit 1.0, the Rust devs want to concentrate on getting the language semantics right. The most important thing is that Rust CAN be made fast even if it is not fast today (but I'm not suggesting that it's not fast but it probably has room for improvement). You can't make e.g. Ruby fast while maintaining backwards compatibility.

Piggy-backing on premature optimization: Yes, 100% agreed. Totally needs to go away. I encounter it most used as a crutch for otherwise skilled programmers to ignore any analysis what-so-ever in any area of software development.

IME they produce late, over-budget, fragile systems because they're averse to thoughtful consideration of the problem at hand and end up rewriting the same thing three times over.

I'm generally not a fan of the term "Software Engineer", but I think attempting to earn the label is worthwhile at least. The "burn someone else's money" mind-set is at the opposite end of the spectrum though. I can't imagine any other field where the "customer" (the person cutting the cheques) would find that acceptable.

> Oh sure, I told you up-front I had two kids and my mother-in-law visits frequently so I want a 4 bedroom house, but you built a 3 bedroom one instead. But that's fine, just go ahead and tear that down and start over. I don't mind paying for both. Maybe by the fourth iteration you'll include a kitchen and bathrooms!

> Then on iteration 5 maybe the bathrooms will even have toilets in them. And don't worry about building codes. We'll just rebuild the house after the under-gauged wiring burns it down. Also, after the sewer gases fill the house we can always break up the foundation and redo the plumbing to add P-traps later.

> It's not like any of this is stuff that you could know up-front. That's why I pay you. The professional. With the "get-to-it" attitude to dive in blindly and show me just how fraught with mistakes this whole thing can be. And you're right, how do I really know I'll want a kitchen up-front? Maybe we'll just eat out every night after moving in. Previous evidence to the contrary not withstanding.

Er... went a little off-tangent. Just needed to get that off my chest I guess. ;-)


Agreed -- I've found these pearls of received wisdom often crumble on non-trivial projects. "Premature optimization" can lead some to defer thinking about performance at all. "Profile and fix the hotspots" without simultaneously thinking about architecture can yield a mass of uniformly slow branchy code with no path forward. It's hard to appreciate until you've been there. However, I do have faith that mozilla folks have most certainly been there and that those lessons inform the rust design.

Yes, I'd say a language runtime definately falls in that critical 3% Knuth referred to in the original quote.

As I understand it, the goal is for Rust to perform as well or better than C++ without the need for an unsafe mode. From the long discussions I've seen on the Rust mailing list, I think the likelihood of a "disable all checks" mode is nil.

Yes, idiomatic C++ which uses the safety features that C++ has to offer, as far as I understand. Not the raw performance that C++ can give you. Just wanted to point that out.

Source: http://pcwalton.blogspot.de/2010/12/c-design-goals-in-contex...

It's the job of the compiler to optimize out things it knows are secure. If an array lookup is known to be secure at compile time, then LLVM already optimizes out bounds checking without the need of a fast mode [1].

If there is not enough evidence available to the compiler that a particular array lookup is not safe, then that evidence is also not available to the programmer. Therefore, one would have to write a manual bounds check anyway. Double bounds checking, one by the compiler and one by the compiler, can also be optimized out.

Maybe Rust is not capable of completely covering all optimization cases right now, but I don't think there's a need for a turn-off-all-checks mode.

Does anybody have any material about how a turn-off-all-checks-mode improved performance for another language? I'm curious about what the difference is.

[1]: https://github.com/rust-lang/rust/issues/9024

> If there is not enough evidence available to the compiler that a particular array lookup is not safe, then that evidence is also not available to the programmer.

That's quite wrong. Humans are capable of deductions that compilers, which must necessarily use conservative, constructive methods, can't dream of. They can also have information that isn't available to a compiler even in principle, although this is less likely to apply to eliminating bounds checking.

Yes, and programmers on a development team change with time, taking their knowledge with them.

So the next guy touching the code just makes the code blow up, due to wrong assumptions.

It's worded quite strongly, but the programmer can only be certain that a certain bounds check is guaranteed to succeed if the compiler agrees. In other situations something about the current context may allow a bounds check to pass, but the "current context" is always bound to change in any sufficiently complicated program.

Currently Rust's perfmance is about that of Java's. Which is and isn't in contention with C++. Something's yes, some no. Rust beats C++ in a few areas on average (tree look ups for example) but that's about it.

It's not to early for it to be a concern. It should be a concern in system languages aiming to compete with lanagues like C/C++/Fortran. But I feel rust isn't in a terrible competition for being at least 15 years late to the party.

Do you have specific benchmarks to back up this claim? I think it's completely unfounded. Every test we've done against C and C++, we're competitive. Exception is the performance of the IO/task runtime, which still needs work, but is unrelated to the language.

There's also the factor of immaturity: the language is young and changing, so it hasn't really been possible/sane to spend the effort writing high-performance micro-optimised libraries for various tasks.

Are the IO/task runtime tests you've seen available to look at somewhere?

I'm thinking of https://github.com/rust-lang/rust/issues/15363 as one example.

Great, thanks.

Well, Rust uses llvm, so unless rustc is generating really poor intermediate code, its performance will be the same as clang C/C++.

That isn't quite true. Swift will be using LLVM as a code generator, but because it will use ARC (a form of garbage collection), it will still end up slower than C++ in many benchmarks. That's not the only example, of course--while LLVM helps immensely, it's not a miracle worker and the original language semantics do make a difference.

This is not true, as Swift ARC is supported by the compiler itself, whereas C++ ARC are library based without any compiler supporter.

So a Swift compiler will be able to optimize away ARC calls deemed unnecessary via dataflow optimizations, which a C++ cannot apply.

I think C++ give you many more tools to avoid using "ARCs", e.g other smart pointers, not needing smart pointers at all etc.

Not using smart pointers is "memory leaks and security exploits" land.

RAII is great, but not when dealing with shared data structures.

So I lack to see how it can be better.

Note that I am a big C++ fan since the C++ARM days, but I am also aware of many of its warts.

I just meant you don't need necessarily need pointers at all, since you put things directly onto the stack (or into vectors/your favourite data structure), no need for additional indirection.

(Maybe Swift offers this too, but it's certainly something C++ excels at.)

The stack doesn't remove the need for heap allocation, since the stack is only useful for things that don't escape their context. The JVM can even do escape analysis and allocated short-lived objects straight on the stack - too bad the currently implemented algorithm is fairly limited.

But anyway, for shared reads or writes between CPU cores, you need the heap. Speaking of which, usage of smart pointers is most of the times less efficient than a concurrent garbage collector if that means reference counting.

C++ excels at fine-tuning the memory access patterns, depending on use-case, which can provide a big boost in performance if you know what you're doing - whether that means stack allocation, smart reference counted pointers, object pools, or whatnot. I don't yet see anything preventing people from doing that in Rust, while getting some extra safety for the common use-cases.

I wasn't meaning to suggest that heap allocation is not useful (e.g. the vectors I mentioned are heap allocations, as I'm sure you know), just, as you say, that C++ offers fine control over memory access patterns.

However, for shared read/writes you don't need the heap: global variables work fine, as does passing around pointers into some thread's stack. (They may not be the best practice, but it's still possible.)

Smart pointers are not just reference counting, e.g. the single owner pointer types Box (in Rust) and unique_ptr (in C++). Furthermore, the majority of the cost with many reference counting implementations is the atomic instructions required for safety, if you can get away with non-atomic instructions then the reference counting probably won't look so bad. (Rust can express a reference counted pointer that is statically restricted to a single thread `Rc`, while also providing the more expensive threadsafe one `Arc`.)

Rust is designed to offer the similar power in terms of controlling memory access patterns.

Sorry, I took your opinion out of context. You're right of course.

Swift does offer this, though in a slightly weird way; structs (and I think arrays) are stack-allocated, classes are heap-allocated. Swift structs are more featureful than, say, C++; they mostly behave like classes, but live on the stack and are passed by value.

Funny to see stack allocation mentioned as C++ strong feature, when so many languages offer it.

Maybe this is a side effect of scripting languages and VM implementations going mainstream.

Please, be direct with your criticisms of me rather than just implying them.

What other commonly used languages offer pointerlessness to the same degree as C++? Rust, for sure. Not C as there are no generics, making allocations + void* and simplistic data structures like linked lists more common than necessary. Maybe D but AIUI there is some struct/class difference that forces GC allocation for classes. What else?

(Also, please read my original comment properly, I was not just talking about "stack allocation".)

I am just answering about placing data into the stack, and that is offered by all descendents of Algol/Pascal/CLU/Mesa/Modula/Ada/... family of languages.

You can place data in the stack in all of them.

Quite a few of them allow for generics, starting with CLU.

Ada also offers RAII, although a bit more verbose than C++ way of doing it.

All of them allow reference arguments, hence no need for pointers for output parameters.

RAII is totally irrelevant to this discussion, and I clearly said I was not just talking about placing data on the stack.

> All of them allow reference arguments, hence no need for pointers for output parameters.

I wasn't very clear, the use of a C-style pointer is irrelevant: if it is being used as a reference (i.e. not owning, no need for clean-up/deallocation), then it counts as a reference. I was using the Rust terminology where 'pointer' mostly refers to 'smart pointer'.

> but because it will use ARC (a form of garbage collection), it will still end up slower than C++ in many benchmarks

Well, depends on the C++. ARC is reference counting behind the scenes; in Objective C and Swift, the compiler inserts retains and releases as appropriate. A lot of C++ code also uses reference counting a fair bit; that's what shared_ptr is for, for instance.

I want a language I would enjoy programming in. Rust seems to have a lot of what I want (GC not mandatory, perf, etc, etc) BUT private-by-default is a terrible idea. I started writing a Rust program and I was just typing pub pub pub all over the place. Ugly. That plus the excessive markup the language requires for safety are enough to put me off it.

Video game programmers desperately need a new language to replace C++, but I think this is not it, because the amount of friction added for safety reasons feels very high.

I'll keep an eye on the language as it evolves, though.

The intent of private-by-default is to better control the API that a compilation unit presents. Rust allows you to inline functions across compilation units, which is incredible for optimization but inflates binaries with AST metadata (and as a result, inflates compilation times) if you attempt to make public every function under the sun. Rust also intends to have an ironclad versioning story, which means that API breakage will be taken tremendously seriously. With public-by-default, it's far too easy to accidentally break your API by modifying something that was not intended to be exposed in the first place. It's the antithesis of reliable software.

You're of course welcome to think that private-by-default is a terrible idea, but let's be aware that features are not adopted without reason. Programming language discourse (in general, not just related to Rust) is all too often dominated by people who seem to think that particular combinations of features are chosen at random out of a hat.

IMO the problem is not private-by-default but small modules and the confusion of scope with packages. (I should say that my Rust experience is non-existent so correct me if my intuition on its semantics is wrong).

It's reasonable that individual Rust files have their own scope. But it is a nonsense to imbue a namespace with an API or versioning story. Programmers do not think that way.

What has an API? A package. Something big enough that you can submit it to a package manager.

Therefore, private symbols in a package should be public to that package. That doesn't mean they should be in scope, but they should be available to be imported by other files in the package.

Public/private would then only be relevant at the boundary between packages.

There's a good reason to do this: it encourages modular design. A small-module language like Python uses modules so much that they miss the opportunity to take on a life of its own.

For instance, Django is split into hundreds of tiny "modules". But no one of them is big enough to be meaningful without the others. There is no singular "Django templates" module, nor a "Django ORM" module. In that sense it is monolithic via over-modularization.

That's a very interesting point of view, but it doesn't quite mesh with my view of the world. I think it makes perfect sense for modules much smaller than "packages big enough to be submitted to a package manager" to have small, well-defined APIs. I'm not sure why thoughtfully limiting APIs would be a good thing at the package level but a bad thing at the module level.

Seems like there's a good case to be made for a 3rd level of visibility between private and public. Similar to Java's package level. I'd like to see (from least to most public):

private: only visible inside the current namespace/module

crate (possible name): importable anywhere in the current crate/compilation unit

public: visible outside the crate/compilation unit

I think the problem is not private-by-default, but the repetition of "pub". We tried public-by-default and it led to a situation where it was very hard to make a stable API for your modules (which we need to avoid breakage of existing code). It could be solved with a "pub { ... }" block (which I'm pretty sure you could write today with a macro if you wanted to). Personally, I prefer repetition of "pub", as it forces me to think about what API I want to present to consumers of my library, but I can see how it can be annoying, especially if your modules are purely for internal use and you aren't concerned about API stability as much.

Regarding safety annotations, there is a WIP effort to eliminate 80%-90% of the lifetime annotations used in practice. There are also a couple of proposals to change the syntax of lifetime annotations to be less noisy.

I certainly welcome the attempts to reduce annotations and will look again at the language in a while.

It is interesting that the design priorities are so library-centric. I agree that library quality is important, but think about this: For every library that someone writes, N people are going to use that library in leaf programs, where if the library is useful N is much much greater than 1. So by definition the vast majority of code is not library code.

In video games we write programs that are between 200k and 2M lines, say. That is big enough that you do want to think about what API you are presenting from part of that program to another, but stability of that (internal) API is almost never a concern, and in fact this is one of the big boons of statically-typed languages: you can change a function and you instantly get errors at all the calling sites, allowing you to be sure you updated them (except in some bad ambiguous situations).

This fluid ability to change how parts of your program talk to each other is very important in large programs. It is one of the major ways you avoid code rot. The more friction is introduced into this process, the lower the ceiling on how large and complex a program you can build.

The other thing about games is that the problems are inherently very interconnected. Yes, we kind of partition things into different modules, but these modules all want to talk to each other very heavily (partly for inherent problem space reasons, partly for performance reasons). So again, friction introduced here hurts us more than it hurts most people.

I understand that private-by-default seems like the right idea because it supports a certain paradigm of programming that is generally considered the right thing (encapsulate your implementation details, modularize as much as possible, etc). But what I have found, in my field at least, is that this is less true than many people think. Yes, it's important to keep things clean, but fluid ability to change code is very important, and overmodularization hurts this. I think that some day this will become more widely understood, the same way that today most good programmers understand that OOP is not some kind of One True Panacea the way folks in the 90s seemed to think.

Good ideas can be carried too far and I think for my field private-by-default is way too far. Having to put 'pub' on every member of every struct and on well more than half my functions is kind of bananas.

(As someone who does not buy into the paradigm of OOP, I want to write functions that operate on data. In C++ sometimes I encapsulate these functions as struct members but this is just an aid to understanding when it is convenient; most functions are declared at file scope. In order to have a functions-operating-on-data mentality in Rust, I guess I need to put a pub in front of every member of every struct, which feels very distasteful; it feels like the language is pushing me toward paradigms that many in my field have tried and subsequently rejected after decades of pain.)

Well, this certainly went a few places, but that is where I am on these issues.

Great, that's really helpful feedback.

I guess the way I see it is that there's a difference between a namespace and a module. A namespace is what you're describing: you want a way to isolate the symbols of different components from one another, but you don't really want ironclad abstraction barriers. A module, on the other hand, is the unit where you really care about presenting a well-defined API. Sometimes projects call for one or the other, or a mix of both. You can always technically model namespaces as modules, but you end up repeating "pub", "public", or "export", or whatever your language calls it a lot. So it may well be good to have both features in the language.

Yeah, other game developers have requested that the safety guarantees be relaxed, because they don't particularly need it and relaxing them would allow for "nicer" code.

This probably just means that Rust isn't appropriate for that domain at the moment. The focus is strongly on memory safety, as that's what security-critical applications need: compromising this may weaken the ability for Rust to overcome C++'s inertia in the space of web browsers and operating systems (etc.), because zero-overhead memory safety enforced by the compiler (not just by convention) is the big draw-card.

Maybe in a world of web-connected multiplayer games handling logins and payments, security will become more attractive; or maybe the fewer memory corruption (etc.) bugs will eventually win the gamedev community over. (I know some gamedevs have been surprised that they have somehow managed to have very few bugs in their Rust code: http://www.reddit.com/r/rust/comments/28mlba/what_is_your_bu... )

I've been watching Rust for a while from the game development perspective and as much as I want it to be, I don't think it's a great fit for that domain. Still rooting for Rust because we really have a lot of work on the security front but I've been having a great deal of fun programming in Nimrod (http://nimrod-lang.org/). It's a bit of a one man show but there's a lot to like.

I really like both of them too. I really like Nimrod. The smaller community and (at least when I last checked) bad documentation especially for async stuff made me worry a bit.

However we should remember both languages are basically pre-release. They are not finished. That will take a while. After that it can take a while for a nice eco system and a common coding style to emerge. Not in the sense of hard rules, but when a community emerges around a language it's a bit like they develop a common accent and vocabulary. From that on other stuff develops, thickening the eco system fixing bugs, developing stable libraries. All of these things take and a lot of it is done by the first people getting into the language, even before it is released.

However I think it will still take a while of people trying to do something completely new with both languages to find and get rid of rough edges, while learning to use the language.

Look at Go for example. They enforce thing with fmt, etc. and it has been in use for a while now, but despite being used in production by many companies the ecosystem is still developing rapidly, there are still known problems to be fixed in future releases, there are still completely new things done with Go, the community didn't really "settle" (of course never happens completely, else there would be stagnation) about certain things, there is (IMO) still no really good IDE, there is still a lot to be made and most importantly there are no "Gurus" yet. Okay, maybe you wouldn't call them gurus, but there is still a hype phase. And if you look back to slightly older languages. Node.js still has a lot of those problems too, but the hype is slowly going away (other than on fronts, like Meteor, etc.), making many things way more rational. This means certain people move away from it, but others are just starting to actually consider it, because things are in place.

You can also look way back. C and C++ have a huge eco system, Perl has a module system where each library you make publicly available on CPAN is tested across a lot of versions, against tons of platforms. Well, you might think about the language in one way or the other, but that are the kind of projects that you really want if you think about using a language in production/enterprise.

In the startup world things are a bit different, cause you often are okay to take certain risks in exchange for development speed for example. Of course that really depends on what you are doing, but I guess it is hard to deny that a lot of (successful and not so successful) startups base upon very new, cutting or even bleeding edge technologies, while older or enterprise companies tend to be way more conservative about that.

I hope that doesn't sound bad, but the defaults in Rust are a really good thing in my opinion, cause it kinda pushes you into having better programming styles. In theory this could be done by a lot of experience self discipline, but in reality that can be hard hard to get, especially in a team.

I think your response is on point because Rust also seems to be a lot of what I want (GC not mandatory, perf, etc, etc, plus pattern matching). Private by default is literally a huge positive for me. But the terribleness of Rust strings is enough to put me right off of it.

I'm worried that all of us are waiting for something to replace C++ that can't happen.

When you say "terrible", are you complaining about having to write `.as_slice()` and `.to_string()` all the time?

For context for others who may not have a lot of Rust experience, Rust has two string types, the heap allocated `String` and the 'string slice' `&str`, which is a view of some string data anywhere in memory (possibly on the heap, possibly not). A little more info about these types: http://stackoverflow.com/a/24159933/1256624

At the moment, conversions between them are via explicit method calls, which can result in verbose code when handling them. An explicit conversion is required even for the super-cheap `String` -> `&str` conversion (which is just repackaging the 3 words (length, capacity, pointer) of String into the 2 words (length, pointer) of the &str).

That is part of the problem. The bigger part of the problem is comparing String's with constants. It simply doesn't work easily.

Also, start trying to do pattern matching on Vec<String> vs Vec<str> (and the wide variety of static, ref, etc versions there in) and you end up spending a lot of effort to do what is very basic in most languages that support generic pattern matching.

Try a simple use case, parse a command line argument array via pattern matching in a Rust program.

Comparing a constant:

  string.as_slice() == "foo"
"Parsing" command line args (it's not as slick as it could be, but it's not entirely horrible... for me as an experienced Rust user anyway):

  match std::os::args().as_slice() {
      [] => println!("need more arguments"),
      [ref x, ..] if x.as_slice() == "-h" => print_help(),
      [ref a, ref b] if a.as_slice() == "-f" => write_to_file(b.as_slice())
      args @ _ => println!("unrecognised arguments: {}", args)
Of course, using a proper parser like getopts or docopt.rs would be better:

- http://doc.rust-lang.org/master/getopts/ - https://github.com/BurntSushi/docopt.rs

I think it gets smoother with practice (as in, with practice you're able to know intuitively when you need .as_slice and when a `ref` is required etc.), but yes, improving the ergonomics will be nice. Dynamically sized types will allow for String to be treated implicitly as a &str in many cases, so, e.g., my_string.trim() will work (it currently requires `my_string.as_slice().trim()`). And there is some loose talk about overloading pattern matching via the same mechanism, which would allow proper pattern matching on String with literals `"..."`.

> support generic pattern matching.

There's some subtlety here, e.g. Haskell doesn't support pattern matching on Data.Vector.Vector or Data.Text.Text (which are some of the closest equivalents to &[T], Vec<T> and String).

A) I understand how to make Rust do what I want with strings. B) Even with your examples, I think you've glossed over some of the even trivial use cases for dealing with Vec<String> vs Vec<~str> (or whatever the current nomenclature is). C) Even with your great comments you've pointed out that String manipulation in Rust is not "natural". Which is something that is a very high priority for me.

That said, easy string manipulation is a high priority feature for me. Non-private data access is a high priority feature for the original commenter. I worry that too many of us have expectations for Rust that are unlikely to be fulfilled.

That's not to say that the Rust team has set too high of barriers, only that we have elevated them to something they aren't. I'm less concerned with what the Rust team is delivering (which so far has been great) than with what people seem to be expecting (which is nothing less than a C++ replacement in all contexts).

The problem is that the string story is on two sides of a barrier erected in Rust very much by intention: To make memory allocation explicit, and to enable allocation-free operations as far as possible

This is why Rust will have you juggle String (a string with allocation) and &str (a "string slice"; a view into an allocation elsewhere) all the time.

Erlang does support pattern matching on binaries though, that that's really nice when splitting out and converting bits and bobs from binary data.

> I'm worried that all of us are waiting for something to replace C++ that can't happen.

From the early days of computing, history proves systems programming languages only get replaced when OS vendors push another language on their official tooling.

Apple is now pushing Swift and Microsoft, at very least, dumping C.

So will Mozilla push Rust as part of Firefox OS? If not, which OS vendor will pick it up?

Note this applies also to other "wannabe C++" replacements.

Rust looks very interesting for the embedded programmer. Imagine a leightweight OS with minimal runtime requirements, similar to Contiki[1], but done right.

[1] http://contiki-os.org/

Given your conclusion there, in what ways do you wish Rust strings were similar to C++ strings?

Sorry, that is a bad reading of my comment (which is my fault). Rust strings are in no way preferable to C++ strings (and in some ways worse). Which is a major failing of Rust FOR MY USE CASES.

That is to say, I had huge expectations for Rust, that it did not (yet) meet. It really made me think about what I wanted from a systems language, and what is possible.

Please don't read this as a screed against the future of Rust, just the present, which didn't really enthrall me enough to learn more of vs C++11

Rust strings are in no way preferable to C++ strings

I haven't been following Rust closely, so this is the first I've heard about the string/slice distinction. But I have spent a lot of time trying to remove unnecessary allocations/deallocations/copies from performance-critical, string-heavy C++ code. I use a combination of custom allocators and slice-like objects, but both of these approaches make it a hassle to interact with code using the natural std::string.

A pervasive distinction between strings that manage their own memory and those that don't, along with language and library support to make using them in combination straightforward would be a big win for me.

I think having felt pain with std::string, you would implicitly understand and enjoy working with the two string types in Rust. They do everything it sounds like you've been doing manually and inconveniently in your C++ code.

Agreed. I'd also make an analogy to C++ where you sometimes must hop between std::string and char*'s with ambiguous lifetimes. Not to mention various custom string types that any big app will have (ex. one that internally exploits jemalloc's allocator optimizations (i.e. exposing block overages), or does rope-style allocation, or intern's into a shared table, or whatever).

> this is the first I've heard about the string/slice distinction

Strings got re-done like ~2 months ago, so don't feel too bad ;)

I ask because I want to know what Rust's pain points are so that I can suggest ways for the devteam to do better. If you have any feedback, please make your voice heard while we still have time to consider it! :)

The history of gamedev (in particular) eschewing C++ for years until the consensus flipped gives me hope that something similar might happen here. The strings thing may take a similar path to so many aspects of C++ -- where it took years of folks like Scott Meyers and Josuttis and Herb Sutter and Alexandrescu talking through the details and rationale before enough people grew to like the decisions C++ made. Think of how many C++ programmers had to get their head around (say) RAII -- it probably first seemed horribly odd and weird, and now it's seen as a fundamental reason why it's a great language.

What "terribleness"?

I think this has more to do with your own personal style of programming, rather than the domain you program in.

If you want to check out Rust and are having trouble because the docs aren't close to done yet, Rust for Rubyists (rustforrubyists.com) just came out with an update supporting 0.11. If you're using the nightlies, the only thing in that book that isn't consistent (that I've found so far) is that the .to_str() method is now .to_string().

I suggest also this: http://rustbyexample.com

It's an awesome resource and updated on a daily basis.

Hopefully, my re-write of the guide will fully eclipse Rust for Rubyists soon. :)

I really appreciate you still updating Rust for Rubyists even though you're working on the official guide.

You're welcome. <3.

0.11 was the first edition I _completely_ re-read all the text and fixed things, and I still had something like ~10 errors. Editing your own writing is hard.

Newbie taking a serious look at Rust here.

Any notes about non-blocking I/O in rust? Iron [1] is mentioned, but am not sure how I/O is handled in Iron yet .. or how it would be written to be non-blocking [2]. I hear "read the source luke" echoing in the chambers of HN and will do that, but some higher level info would be nice.

[1] http://ironframework.io/

[2] http://www.reddit.com/r/rust/comments/1v2ptr/is_nonblocking_...

edit: This (http://doc.rust-lang.org/native/io/) seems to suggest that the norm is non-blocking I/O which is automatically handled by the scheduler.

If you explicitly use the "green" runtime that ships with rust instead of the "native" runtime, you get green threads where the built-in blocking IO only blocks the green thread but lets the OS-level thread carry on executing other green threads.

Internally that is built on top of libuv for async IO, but as far as I know, Rust currently doesn't expose any interface for manually doing async or nonblocking IO.

It seems this issue was only recently closed (May 9) [1] .. and it seems a websocket library with full duplex support is still in the works.

[1] https://github.com/rust-lang/rust/issues/11165

Thanks for this clarification.

This is welcome news.

Also, I wasn't sure that Vec<T> was actually ~[T], I couldn't find that in the docs. Thanks for spelling it out.

I see Rust as a language that could become the “next C” (only better), and I'm excited watching it evolve.

Is there a capability or plans to be able to export a header file from Rust code that C code (or Python, Go, whatever) could reference? I couldn't find anything when searching for it but it seems that it'd be possible since Rust doesn't have a runtime, right?

> Rust doesn't have a runtime, right?

Rust does have a runtime (used for tasks and GC'd types amongst others), the runtime can be disabled and the standard library is being split between "requires runtime" and "does not require runtime" (with as much as possible being moved to the latter).

It's incorrect to say that Rust does not have a runtime, but it's correct to say that runtime-less Rust is very much a first-class concern for the project.

PS: note that C does have a runtime as well, if a very minimal one.

Yes, you can expose C symbols from a Rust library:

  pub extern fn rustlib_increment(x: i32) -> i32 { x + 1 }
#10350 covers actually printing a .h file from Rust code. https://github.com/rust-lang/rust/issues/10530

Awesome. I think the ability to write a nice .h file, in particular, will really be the killer feature for Rust as "better systems language". Exporting headers and symbols for higher level languages in a format that's universally understood.


Unfortunately, all those other things don't fit into the classic Haiku:

A systems language / pursuing the trifecta / safe, concurrent, fast

Oh, wow, someone responded? I deleted that comment because i didn't feel it was sufficiently on-topic, but I had said:

>Ada 95 achieved this two decades go, ATS some time after. Rust is trying for a fourth goal, to be expressive and convenient to use, and a fifth goal, to be portable and available to everyone.

> Safe, concurrent, and fast just isn't enough.

Also, consider putting in a little more tension:

rust: functional stuff

Concurrent, safe and clever

still fast and easy.

> A systems language / pursuing the trifecta / safe, concurrent, fast

I dare to say Ada and the respective compilers meet all those requirements.

I'm very curious, because I don't know much about Ada.

How does Ada ensure that taking a reference/pointer to an element of a vector is safe? (i.e. the vector doesn't get reallocated/deallocated while the pointer still exists etc.) The three options I can think of are:

- pervasive garbage collection,

- disallowing such references and just copying the data out every time, and

- the borrowing/freezing system of Rust

Really only the latter is acceptable for the sort of 'fast' that Rust is aiming for.

Rust also allows for memory safe concurrent code. Message passing is safe due to move semantics and transferring ownership of data between threads, and shared memory is safe because the type system has the power[1] to ensure that any mutation of it is atomic (either using atomic data types, or mutexes). Does Ada allow for both 100% memory-safe message passing and 100% memory-safe shared memory?

[1]: http://doc.rust-lang.org/master/std/kinds/trait.Share.html

As I understand it, Ada can't ensure that. The safe subset of Ada doesn't have allocation with multiple references. SPARK is mostly designed for things like avionics controllers, which tend not to allocate.

I cannot answer your questions in detail but here you find some useful information about Ada:




Sorry, those links are far far too broad to be useful for answering my specific questions for someone unfamiliar with Ada (like me).

I took the time to Google a little, and as far as I can tell, Ada just doesn't allow you to take a reference (aka 'access') into a vector: http://wiki.ada-dk.org/ada.containers.vectors#vectorselement I don't know if this means that it copies elements for every access or there is some black magic happening on the inside.

Similarly, it seems that the only shared memory is so-called 'protected' types, which compulsorily have a mutex around every interaction, even if the shared memory is entirely immutable, or if they are some other sort of atomic type.

As such, it seems that Ada doesn't necessarily have the same claim to those adjectives as Rust does. (Of course, I know next to nothing about Ada, so could easily be wrong.)

Do you mean something like this?

    with Ada.Text_IO; use Ada.Text_IO;
    with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
    procedure example is
    data : array (1 .. 10) of aliased integer;
    dataPtr : access  integer;

        for i in 1..10 loop
            data (i) := i;
        end loop;
        dataPtr := data(4)'access;
        Put("The array 4 is ");
    end example;

No. I'm interested in a dynamically allocated vector, where you take a pointer/reference/access to one of the elements and then try to add some more, which may cause the vector to reallocate and move in memory. Naively, this will leave the access dangling, pointing at invalid memory (which is not safe).

The three solutions I mentioned: GC protects against this by not deallocating the original array. Disallowing taking references makes the whole situation impossible. Rust protects against it by disallowing mutation of the vector while the reference exists.

I'll be more interested when they've found a way to work with native binaries on windows, and handle creating/using binaries that take full advantage of the PE's import/export functionality. In reality it's not cross-platform if it doesn't support the almost guaranteed necessary features of the platform -- utilizing compiled dynamic libraries.

Open an issue. There is really only one contributor who works on Windows support. We need more people telling us what we are doing wrong on Windows.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact