> among the sites of its peers, only Ruby is similarly localized. Given that several prominent Rustaceans like Steve Klabnik and Carol Nichols came from the Ruby community, it would not be unreasonable to guess that they brought this globally inclusive view with them.
Not just that, I actually looked at how Ruby's website did it in order to bring it to our site. This has... issues. But it's served us fairly well.
Additionally, while there's so much more I could say about this post, one thing:
> my naive Rust was ~32% faster than my carefully implemented C.
This is a metric I'm more interested in, far more interested than classic benchmarks, where various experts eke every last little bit out of an example. What's the average case here?
It is, of course, much much harder to get numbers for these kinds of things...
One thing that we have noticed in our work on our Rust AV1 encoder, rav1e, is that Rust autovectorizes a lot better than C. In particular, it does a better job of proving that slices have nice sizes (e.g., multiples of 8 or 16 elements). That allows it to leave off all of the boilerplate for handling odd sizes. Rust also does not automatically promote every arithmetic operation to an int. That makes working on 8- or 16-bit types a lot more efficient. The stronger type system, memory safety, and the preference for idioms that avoid bounds checks mean the compiler can successfully autovectorize a lot more code than C, too. We used to joke that the only C code that gets autovectorized is the initialization loops that run exactly once and just set everything to zero.
It's still not as good as hand-written SIMD (maybe within a factor of 2), but it's a lot better than no SIMD.
That said, it makes it hard to do straightforward performance comparisons with C, especially for code where you know you will write the hand-written SIMD, but you just haven't gotten to it yet. There is a lot of code like this in a freshly-implemented video encoder. Rust is using a tool, autovectorization, that it won't get to use when you manually vectorize, and C isn't. That threw off a lot of our early attempts at benchmarking Rust vs. C.
Of course, most things are not video encoders, and most code won't be manually vectorized, so the better autovectorization is an absolute win in the real world.
What C do you mean? Compilers aren't equal: https://github.com/Const-me/MemoryAccessCosts
I would be interested if they're wrong, hehe
Embedded can be tricky at times; we've had a working group pushing on making stuff great all year. There's still a lot of work to be done. Sometimes you need to opt into nightly-only features. We'll get there!
(One major example is platform support: since we're built off of LLVM, we may not have a backend for more obscure architectures. ARM stuff works really well, generally. AVR is down to some codegen bugs...)
The real questions are whether Rust would improve the performance of things like a Bluetooth Low Energy communications stack given that communication stacks are probably the most complex things that run on a Cortex M0 or M4 class processors.
The metric "my naive Rust" vs "my carefully implemented C" might be OK, once you figure out a consistent way to check code passes as "my naive Rust" or "my carefully implemented C".
Until then it's just an anecdote.
Might there be programmers whose "naive Rust" performs a lot worse than their "carefully implemented C"?
Might that be unsurprising to them and uninteresting as a blog post?
In all fairness... the "carefully implemented C" was all wrapped up as an external lib for the node application they reimplemented so no having to convert values to/from js and deal with all that other stuff.
One of my pet peeves with several "newer" languages that I've looked at is that users don't usually talk about the ugly parts. You can try out C++ for a week visiting forums and r/cpp and you find out fairly quickly what the pain points are and what typical workarounds look like. At that point, it is up to you to decide to what extent you can live with those downsides and where to tread lightly. Same for C, Python, OCaml etc.
To be clear, I don't mean that you should go looking for the bad parts in a language but I do believe you should be aware of them before you invest a lot of your time in it. Unfortunately, a lot of the language love blog posts do not talk about the pain points of the language and what kind of problems it isn't well suited for.
I like Rust and I love several features that Bryan talks about (algebraic data types being one of them) but I would love to read a more balanced evaluation of the language - focusing on aspects which are rough around the edges and expected future improvements.
My two major pain points in Rust are (1) the compile times, (2) the high variance of quality in the ecosystem.
The compiler for Rust is very slow, it takes minutes to build software from scratch. Things are getting better with incremental compilation, but it's definitely not as fast as D or Go to compile, and that can be very grating during development.
Anyone can easily contribute to the crates ecosystem and post their libraries and programs to crates.io. Unfortunately, there is no real way to know what's production-quality and what's a toy. You can try and rely on the number of downloads, the names of the contributors, etc., but there is no system that tells you what other Rustaceans think of a crate. For instance, I tried using an HTTP client with few dependencies (because the most downloaded option, reqwest, has a lot of dependencies), but I found that (a) the library issued a read(2) syscall for each byte that it downloaded, (b) did not always return the full payload for HTTPS. There was no way I could tell from just looking at the crates.io page.
I am also looking forward to the incremental compilation improvements.
Still think that until cargo actually supports binary dependencies, the experience will not be as fast as it could be.
One routine source of pain was when one of your upstream dependencies changed its dependencies. That would happen quite routinely. All was fine, unless you actually had two packages that had dependencies on different versions of a library.
You could work around it by pinning the version of the dependency, but of course that's risky. You don't know if you're exposing yourself to bugs, because you're making software run with a dependency version it hasn't been tested against.
Pretty much every build file I ever saw in Amazon had at least a half dozen or more dependencies pinned. Every now and then you'd find you were getting a new dependency conflict, and that things had become an impossible knot to untangle. All you could do is unpin _everything_ and then figure out all the version pins you needed again from scratch.
I swear I would lose at least one to two days a quarter doing that. The development focussed teams would spend way more than that on fixing conflicts.
I started out with Rust just last weekend. Put a couple of dependencies in the Cargo.toml library and got stunned when it pulled in over 400 dependencies, a number of which I'd expect to have seen in the stdlib, not left to the vagaries of random people's implementations.
That said, I can give you at least one thing where Rust needs to, and will be, improving over the next months: the async story is finally settling into place, but hasn’t settled yet. Async/await is coming and will improve things so so much. Right now things are a bit boiler-platey, and you can’t always write what would be equivalently idiomatic to the sync code. See here: http://aturon.github.io/2018/04/24/async-borrowing/
- Type inference pretty much being killed by method calls. For instance, code like this won't work:
fn x() -> Vec<i32> {
let mut x = (0..3).collect();
x.sort(); // Calling any method of Vec
x // Cannot infer that `x` is `Vec<i32>` because a method was called
}
- Turbofish syntax is ugly. For instance, in Rust you say `std::mem::size_of::<T>()`. It would be nice if you could replace `::<` with `<`.- Negative modulo follows C semantics. This means `-2 % 12` is `-2`, not `10`. There is a sane implementation in `mod_euc` method, but it's not the default.
- Lexical lifetimes reject code that should be valid necessitating weird workarounds. NLL will fix that one.
- Trait objects types used to be written using a trait name like `Display`. There is a new syntax which is more clear: `dyn Display`, but for backwards compatibility reasons the old syntax is accepted.
- Macros (including procedural derives) cannot access private items in a crate, requiring workarounds like exporting private items in a crate, and having `#[doc(hidden)]` attribute to hide them from documentation.
- Trait version incompatibility issue. That one is weird, but essentially it's possible for a program to have two versions of the same library. It's possible for a library to say, have a function that requires an argument implementing `Serialize` interface from serde 0.9. If you try to implement `Serialize` interface from serde 1.0 then you will get an error message complaining about not implementing `Serialize`, despite you having implemented it, just in the wrong version of a library.
- Missing higher kinded polymorphism.
- Missing const generics.
- The compiler is slow. Like, really slow.
- The language is hard to learn because of many complicated features like ownership and borrow checking. That said, I think those features are a good thing, I'm missing those in other programming languages, but they are problematic when you are learning the programming language.
But really, there is much more I would complain about in other programming languages, so it's not that bad.
- RC<RefCell<data>> for lambdas to access struct data, specially painful on GUI callbacks
- lack of support for binary libraries on cargo
> This book digs into all the awful details that are necessary to understand in order to write correct Unsafe Rust programs. Due to the nature of this problem, it may lead to unleashing untold horrors that shatter your psyche into a billion infinitesimal fragments of despair.
It's surprising how many things in that book don't have anything to do with unsafe per se. It's just that, when you can't rely on the inference system to "do the right thing", you now have to actually think about subtype variance and the order that destructors run in, instead of letting the compiler worry about all that bookkeeping.
Try writing some Rust, it will surprise you in a few ways. These are not warts (though there are some), but more features of the language. For some these features, move-by-default, single-mutable-reference, strongly-typed-errors, they will be struggles to deal with initially.
It's a language that restricts you in many ways, and this is startling for many. It was for me for sure.
Good IDE support, with autocomplete and integrated debugging, was also missing but that should have improved i believe?
> These values reflect a deeper sense within me: that software can be permanent
Maybe it can be permanent, but it shouldn't be. Software is disposable and all the time developers are mistakenly fighting this aspect of its nature instead of embracing it. Great software is malleable and develops, over time, to adapt to the human that is using it. But it is impermanent--parts are snipped off here, fleshed out there, nothing stays the same, it is obsolete as soon as it is released. Don't delude yourself that your software will run for a thousand years, be like Warhol and celebrate the ephemera that is pretty much every program ever written.
> I have believed (and continue to believe) that we are living in a Golden Age of software, one that will produce artifacts that will endure for generations
Museum pieces, sure, but do we still want to be using generations-old software in the years to come? Hope not. Times change, needs change, and software that doesn't change is replaced by software that does, and quick. What about such monumental artifacts as 'cc', 'awk', or even the UNIX kernel? For years they have dominated the landscape, they are the Ozymandias, the King of Kings. If we are still clinging to these titans in another 20, 50 years, is that a good thing?
*BSD, vim, Emacs, Perl, C, Apache & Linux in some way, gcc, the GNU userland, Air company & banks infrastructure come to mind, and they still do the work.
Another take: what is the ratio of the same software still in use after, say, 10 years to all software in use? I would argue that more than 99% of all software (e.g. by version number) is no longer in use after a mere 5 years. Software is inherently disposable, let's not pretend we're building bridges that will stand for generations.
My point is that developers (I'm one) have a hard time with the qualities of software: we don't understand the nature of software change, and bicker about what bumping a semver number means, and we fight its disposable nature by engineering it to the point where it could run for a decade (it won't).
awk, vim, whatever Dos 3.1 GUI the guys at B&H use to fulfill my orders, why shouldn't I expect to be using these programs in 50 years? They solve a problem and work every time.
No reason at all, if they still solve the same problem, they're still the best solution. The problem with 'problems' though, is that once they're cornered they tend to change into a whole new problem. A nasty one the current solution won't work for, usually.
Is this idiomatic? It seems powerfully indecipherable/illegible
let sum = vec_of_ints
.iter()
.filter(|x| x > 5)
.map(|x| x.pow(2))
.take(3)
.sum()
That is, iterate my vec, only consider values > 5, square them, take the first 3, them sum them.Consider the imperative code:
let mut sum = 0;
let mut n = 0;
for x in vec_of_ints {
if x > 5 {
sum += x.pow(2);
n += 1;
if n == 3 {
break;
}
}
}
I think the chained combinator approach is much easier to see exactly what's happening (and is often higher performance!) state
.db
.send(Queries::GetTopTenHottestYears)
.from_err()
.and_then(move |res| {
result.push(res.unwrap());
state
.db
.send(Queries::GetTopTenColdestYears)
.from_err()
.and_then(move |res| {
result.push(res.unwrap());
state
.db
.send(Queries::GetTopTenHottestMonths)
.from_err()
.and_then(move |res| {
result.push(res.unwrap());
state
.db
.send(Queries::GetTopTenColdestMonths)
.from_err()
.and_then(move |res| {
result.push(res.unwrap());
fut_ok(result)
})
})
})
})
.and_then(|res| Ok(HttpResponse::Ok().json(res)))
.responder()
https://github.com/actix/examples/blob/master/async_db/src/m...My suspicion is the people do this to avoid "variable does not live long enough" type errors.
Iterators are a typical example of this.
vec![0, 1, 2, 3].into_iter() .filter(|x| x % 2 == 0) // Keep the evens .map(|y| y + 1 ) // Add 1 to elements .rev(); // Reverse the iterator
Adding intermediates would just add noise for code like this.
int.__add__(a, b)
is less idiomatic than a.add(b)
which removes the global abstract. Though I see it looks less symmetric.I'm not a Rustacean - I only speak to the syntax difference.
Could anyone suggest where to start developing a cross-platform GUI program in Rust?
They made some good decisions like using real native controls and writing it in c to allow bindings to many languages, including 3 for rust. The biggest catch for me is the lack of table controls: https://github.com/andlabs/libui/issues/310
I wonder what if in https://www.reddit.com/r/rust/comments/9bapwt/thoughts_on_wh... how about use Rust for most of the UI logic and off-load the final steps for a native layer. I call it a "almost cross-platform" UI.
I've seen promising things about Qt/QML. Qml in particular helps avoid having to deal with lots of C++ interop.
GTK seems to be rallying around Rust, and it actually looks pretty good nowadays. relm is a really interesting elmish wrapper around GTK.
Others have put rust components behind an Electron GUI.
So tl;dr it's quickly moving from "terrible and immature" to "terrible, like other languages".
For simple GUIs, the problem I always ran into was 1) spooky licensing vs 2) Tkinter vs 3) can this be forced into a browser?
> in particular helps avoid having to deal with lots of C++ interop.
I agree there is significant demand here. But I also feel a second (smaller) group wants access to GPUs for high-performance visualizations and/or games, since that's usually built in C or C++.
> "terrible, like other languages"
This made me laugh. I want to move out of the browser and back to the metal.
I have used Qt a fair bit for hobby projects, and quite like it in the context of cross-platform C++. But I haven't yet wrapped my head around what a Rust interface to Qt would look like, and results searching https://crates.io for Qt bindings had kinda scared me off.
https://github.com/KDE/rust-qt-binding-generator looks quite interesting though!
> So tl;dr it's quickly moving from "terrible and immature" to "terrible, like other languages".
Ha! Well, at least we don't have so long to wait, I guess.
Could someone share who the competitor and what the knock off was?
https://www.openhub.net/p/systemtap/contributors?page=4&quer...
IMO an operator that returns is pretty magical. What do others think about '?'? It's certainly more ergonomic than pattern matching / monads etc, but it also seems easy to miss if it's buried in the middle of an expression.
I think Swift's try is a bit nicer to use, because it comes at the beginning of the statement, provides more flexibility in how the error should be handled, and supports multiple expressions that may generate errors. On the other hand Swift's error handling is an addition to its type system, instead of being built inside it like Rust.
So `foo?.bar` is something like
match foo {
Ok(inside_foo) => Ok(inside_foo.bar), // .bar the lambda.
Err(v) => Err(v),
}
The `do` notation makes it a bit more explicit. It also looks directly lifted from Haskell's approach to work with monads. sanitize(getFile(getName())?.contents);
Swift requires prefixing the line with try, which makes it easier to notice: try sanitize(getFile(getName()).contents)
Both are a big improvement over exceptions which have no indication at the call site.On the other hand error handling can distract from the intention of the code, so in another sense ? makes it easier to read.
The author can choose if they prefer ?, try!, or a plain old match or if let / return on the Result.
Here's his 2011 diatribe against Oracle (after they bought Sun, which he worked for at the time):
It wasn't the right choice for Rust, but that doesn't mean Go's choice was wrong.
I think both languages made the correct decisions for what they're trying to accomplish as a language.
In fact, there's even a callout to Result<> "..but the only thing you can't do, is ignore them" . :)
I really need to get my hands dirty with Rust. A lot of very smart engineers have been throwing projects my direction seeking opinions and I've never been able to properly offer any. The folks working on the language and projects around the language are really sharp.
Recently I haven't had any personal projects that aren't a conglomeration of API requests and simple data storage. Rust is more than capable of doing this kind of work but I'd really like to find a pet project that would take advantage of it's performance capabilities.
I can't denied rust is hard and a lot of time I get lost thinking "why rust don't let me do...". But is truly a good teacher...
> These values reflect a deeper sense within me: that software can be permanent — that software’s unique duality as both information and machine afford a timeless perfection and utility that stand apart from other human endeavor.
I wish I had a copy (..link) to that article. Byte, Computer Shopper... something like that.
There are a few pieces of code I wrote in the 90's (in C) that I know are still in use and may survive a few more decades; as long and the companies that I worked for at the time are still in operation anyhow.
There's a learning curve, but with much experience, I find myself naturally thinking and writing in Rust code. I can get in the flow, and it feels just as high level and productive as languages prized for those qualities. I'm happy to see other very talented programmers have similar experiences.
All successful systems programming languages got OS adoption at a given moment on their history.
As addendum, even if off-topic, Microsoft is also making use of it on Azure IoT Edge and possibly on Azure Sphere, but the later remains to be confirmed.