
Falling in love with Rust - irfansharif
http://dtrace.org/blogs/bmc/2018/09/18/falling-in-love-with-rust/
======
steveklabnik
This post makes me extremely happy. :D

> 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...

~~~
k__
I talked to some embedded C programmers and they said thad basically
everything besides C is too slow for their use-cases.

I would be interested if they're wrong, hehe

~~~
steveklabnik
In general, if Rust is significantly slower than equivalent C, it's a bug. We
do care about bugs, and if you try some stuff and find them, please file them.
(We track them via this tag: [https://github.com/rust-
lang/rust/labels/I-slow](https://github.com/rust-lang/rust/labels/I-slow))

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...)

~~~
k__
Are there theoretical boundaries that prevent Rust from ever reaching C
performance or is this just a practical question of compiler optimization?

~~~
steveklabnik
It really depends on _exactly_ what you mean.

There's one significant thing that's in C that's not in Rust: alloca. If you
need that, well, we've talked about it, but haven't accepted a design, and
some people never want to gain it directly.

EDIT: another comment pointed out goto; I have no idea what the performance
implications are but I’ll have to add that to my list of “stuff C has that
Rust doesn’t.”

Sometimes, you need nightly-only features. This means that, in some sense,
today's Rust is slower, but tomorrow's Rust may not be.

Sometimes, "equivalence” is the issue. You _can_ turn off bounds checking for
array access, for example, but most Rust code doesn't, for hopefully good
reason. Sometimes those checks are elided, and so it's exactly the same.
Sometimes they're not. Sometimes they're not and that inhibits other
optimizations. Is unsafe Rust "equivalent" to the C? Or does that not count?
(Unsafe Rust is not always faster than Safe Rust...)

At the theory level, I can't think of anything off the top of my head that
would inherently make Rust slower than C, generally speaking. There's also
some degree of argument that in theory, Rust should be _faster_ than C, thanks
to how much more we know about aliasing, etc. That's a whole other can of
worms...

~~~
simcop2387
> Sometimes those checks are elided, and so it's exactly the same. Sometimes
> they're not.

This is something I've personally thought about a lot and I have no idea how
I'd actually want it implemented either on the backend or in the syntax, but I
think it'd be useful to have some way to say "I want these bound checks to be
elided, if they can't be then make that an error". Similar to how you'd
decorate a pure function in other languages to say that it can't cause side
effects or depend on side effects for what it's doing.

I think that's probably a high level ask that's probably significantly more
difficult that it seems initially too.

~~~
andrewflnr
You probably just want a dependently typed language. Proving bounds at compile
time is an introductory exercise for the likes of Idris.

~~~
zem
ats [[http://www.ats-lang.org/](http://www.ats-lang.org/)] is a C-level
language with dependent types

~~~
lodi
Note that ATS has a limited version of dependent types as compared to
Idris/Agda/Coq/etc. As I understand it, you can't run arbitrary (recursive,
etc) functions at the type level. But even this 'lite' version lets you
express preconditions like "n is a multiple of 4" or "the source array and
destination array must not overlap in memory".

------
raphlinus
For what it's worth, these observations track closely with my own experience.
I like that Cantrill emphasizes values rather than simply technical decisions.
The Rust community, I believe, tries to get things right.

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.

------
gshrikant
I've been on something of a language tour recently - trying out C++,
Typescript, OCaml each for a couple of weeks just to get a taste of living in
each ecosystem. I haven't been actively following Rust beyond reading Hacker
News articles (of which there are many) about people liking the language.

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.

~~~
gnuvince
Language-wise, Rust is really top-notch, I find it hard to fault any design
decision. There are some design choices that annoy me (e.g., two closure types
can never be equal), but there is always a fact-based rationale for why things
are the way they are.

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.

~~~
Twirrim
The dependency thing worries me to a fair degree. Amazon has (or at least had
when I was there) a build tool fairly similar to cargo. Libraries and software
was imported in to the underlying repositories with a version associated. You
put that in your list of dependencies and voila when you built your code
everything got neatly combined and compiled as needed.

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.

~~~
pornel
For native Rust libraries this is a solved problem. Cargo finds one common
compatible version of each library that satisfies requirements of all
dependencies, and only when that isn't possible, it allows more than one copy
of the library (and everything is strongly namespaced, so there's no
conflict).

And it has a lockfile, so your dependencies won't update if you don't want
them to.

The only problem is C dependencies like OpenSSL that do break their API, and
don't support multiple copies per executable, so there's nothing that
Rust/Cargo can do about it (you as a user can avoid them and choose native
Rust libs where possible).

------
qaq
Honestly watching Brian talk about Rust
[https://www.youtube.com/watch?v=LjFM8vw3pbU](https://www.youtube.com/watch?v=LjFM8vw3pbU)
is what finally inspired me to get the books and give it a go.

~~~
yash1th
Can you tell what books/resources you have used to pick rust?

~~~
qaq
Honestly so far "The Rust Programming Language" book proved to be the most
useful resource.

------
whalesalad
I like that the author threads in so many other concepts with links to other
blogs that explain those concepts.

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.

~~~
mamcx
For me, is build a relational language
([http://tablam.org](http://tablam.org)) (like a kind of data engine, like
sqlite but not storage). I never do that kind of work before (ie:
language/rdbms) and is a eye opener.

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...

------
wasted_intel
I'm happy that error handling landed in first place; I feel the same way. Rust
is the first language I've used with ADTs, and they're incredibly expressive.
It allows the error system to be a set of idioms built atop an expressive type
system (okay, so the ? operator does add a _little_ sugar), rather than a
discrete language feature, like with exceptions. It brings error handling to
the forefront, and it's a _big_ reason why people tend to say "if it compiles,
it works" when talking about Rust.

------
bacon_waffle
One thing I've been missing in starting with Rust, is a basic GUI library.
Conrod looks promising, but the apparent lack of documentation, coupled with
being very new to the language, makes for a very steep learning curve.

Could anyone suggest where to start developing a cross-platform GUI program in
Rust?

~~~
mastax
On mobile, so no links for you ;)

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".

~~~
bacon_waffle
Thanks for the pointers!

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](https://crates.io) for Qt bindings had kinda scared me
off.

[https://github.com/KDE/rust-qt-binding-
generator](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.

~~~
oever
There's a new blog on Rust Qt Binding Generator with the most feature-rich
application in it yet:

[https://www.vandenoever.info/blog/2018/09/16/browsing_your_m...](https://www.vandenoever.info/blog/2018/09/16/browsing_your_mail_with_rust_and_qt.html)

------
skybrian
It's kind of weird to say m:n threading is terrible when it seems to work okay
in Go.

It wasn't the right choice for Rust, but that doesn't mean Go's choice was
wrong.

~~~
Rusky
Go solved the main downside the article cites with M:N threading- it replaced
split stacks with growable stacks, via a precise garbage collector.

~~~
kibwen
That solved the hot split problem, but that was more of a once-in-a-while
gotcha. The main downside for Rust specifically was the complications that
arise when doing any sort of C interop in the presence of growable stacks.
This mattered more for Rust than Go, since Rust is relatively more intended to
freely intermingle with C code (and other languages via, C FFI), whereas the
happy path for Go tends to involve exclusively Go code in a given process
(even going so far as to reimplement their own libc in Go), and use of cgo is
discouraged by the community ([https://dave.cheney.net/2016/01/18/cgo-is-not-
go](https://dave.cheney.net/2016/01/18/cgo-is-not-go)).

------
bogomipz
>"Over a decade ago, I worked on a technology to which a competitor paid the
highest possible compliment: they tried to implement their own knockoff."

Could someone share who the competitor and what the knock off was?

~~~
IronBacon
Looking at the blog name I would say DTrace on Solaris (Sun) and SystemTap on
Linux.

~~~
AceJohnny2
Good find. Indeed, looks like Graydon did work on SystemTap in that timeframe:

[https://www.openhub.net/p/systemtap/contributors?page=4&quer...](https://www.openhub.net/p/systemtap/contributors?page=4&query=&sort=name&time_span=)

------
pjmlp
This is great sign for Rust, as it is getting adopted by actual OSes.

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.

------
_sh
My thoughts on the philosophical aspects of the article don't really relate to
Rust.

> 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?

~~~
ethelward
> do we still want to be using generations-old software in the years to come?

*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.

~~~
_sh
There are a couple of ways to look at this. Philosophically, to pick any one
of your examples, my emacs is version 26.1. Is this _the same software_ as
emacs 15.10 released April 1985? Will a perl 6 program run on perl 5? Am I the
same person I was 5 years ago?

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).

------
brian-armstrong
One of the things that's turned me off about Rust is that a lot of code
seems().to().chain().lots().of().operators()

Is this idiomatic? It seems powerfully indecipherable/illegible

~~~
gamegoblin
It's idiomatically done with newline separators, e.g:

    
    
        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!)

~~~
Spiritus
That's nice and all. But then you have abominations like this:

    
    
        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...](https://github.com/actix/examples/blob/master/async_db/src/main.rs#L46-L76)

My suspicion is the people do this to avoid "variable does not live long
enough" type errors.

~~~
gamegoblin
I believe that is written that way because it's using futures. Once
async/await lands this year, all of the futures combinator madness goes away.

------
Animats
Maybe Rust has passed through its dark time and come out the other side. There
was a long period during which error handling was a mess. First it was too
verbose. Then came "try!", which was kind of a hack to return from functions
on error. Now there's "?", and a reasonable structure around how to use it.

~~~
majewsky
It depends on what your field is. If you're writing any form of async code
that uses futures, Rust is still firmly in the dark ages. I'm being told there
is preliminary async/await support on nightly, but it won't be on stable this
year.

~~~
Animats
If you're writing async code with futures, you're probably writing a web
service. Go is a better fit for that.

~~~
majewsky
I'm using both Go and Rust extensively. What I'm doing in Rust, I'm doing in
Rust for a reason. Async code is not just for web services.

------
azhenley
I've been dabbling in Go, but I keep wondering if I would prefer Rust after
reading articles like this...

~~~
innocentoldguy
I dabbled in Go for a while too, but found I much prefer Rust, Elixir, and
Crystal (even though it is still a bit immature). I know a lot of people love
Go, but these other three languages speak to my soul.

~~~
PopeDotNinja
How does the concurrency model in Rust hold up when compared to Erlang/Elixir?
Crystal's concurrency seemed lacking the last time I looked (which was a while
ago).

~~~
steveklabnik
The biggest difference is that Rust doesn't force a specific one on you. The
language knows nothing about threads. It does have two special traits, Sync
and Send, that should be used by any thread-related APIs for concurrency
guarantees, and the stdlib thread API does use them.

But there are other concurrency/paralellism models beyond std::thread,
including really cool stuff like "scoped threads", and more classic "single
threaded event loop" stuff, or thread pools.

The big differentiator is that Rust proves code free from data races at
compile time. Not the specific model.

Incidentally, you can write NIFs in Rust...

------
nixpulvis
This resonated with me a lot:

> 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.

~~~
topspin
I recall reading a story about IBM systems programmers writing code for
zSeries mainframes. A thing that stuck with me was a discussion about code at
the core of this work; virtual memory, IO subsystems, etc. The engineer
reflected on how the code he was writing would still be in production when he
died. It would survive evolution of the hardware for many generations; they
designed it to do this and provide permanent APIs to relying code.

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.

------
millstone
nit: the author describes the '?' operator as both not-magic and magical.

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.

~~~
james-mcelwain
I'm unsure what "miss" means here. If you forget to add a ?, it's a
compilation error.

~~~
millstone
When I'm reading code, I may not realize that an expression can return,
because it can be hard to spot a ? embedded in the middle of the line:

    
    
        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.

~~~
kibwen
Bit of a correction, I believe that `try` in Swift must precede an expression
that can throw, rather than preceding a statement that can throw, so AFAICT
the Swift code would have to be written like this:

    
    
      sanitize((try getFile(getName())).contents)
    

This sort of illustrates why Rust moved away from its own prefix `try!` macro
to its postfix `?` operator: in a language that encourages chaining methods,
`foo()?.bar()?.qux()?` looks better than
`try!(try!(try!(foo()).bar()).qux())`.

~~~
dbaupp
No, `try` can be applied to any expression, and will "try" all throwing calls
in subexpressions of that:

    
    
      func f() throws -> Int { return 0 }
      func nothing(_ x: Int) {}
      func g() throws {
        nothing(try f())
        try (f() + f())
        try nothing(f() * f())
      }
    

This includes method chains:

    
    
      struct Foo {
        init() throws {}
        func bar() throws -> Foo { return self }
        func qux() throws -> Foo { return self }
      }
      try Foo().bar().qux()

~~~
kibwen
Cool, thanks, I looked in the official docs but didn't notice any mention of
this feature. :)

------
markpapadakis
Watch the Apple - Think Different commercial at
[https://www.youtube.com/watch?v=cFEarBzelBs](https://www.youtube.com/watch?v=cFEarBzelBs)
, and pay attention to the script/words. It's as if it was purpose-written for
Rust.

In fact, there's even a callout to Result<> "..but the only thing you can't
do, is ignore them" . :)

~~~
coonery
Jesus...

~~~
markpapadakis
It was mostly sarcasm really — I thought the word play was fun:) kidding
aside, I am intrigued by Rust. I am glad it gets the attention that it
deserves.

------
TuringTest
I'm I the only one reading the title in Elvis voice?

