
How Rust Is Tilde’s Competitive Advantage [pdf] - steveklabnik
https://www.rust-lang.org/pdfs/Rust-Tilde-Whitepaper.pdf
======
Nelkins
So, I've been eyeing Rust for a while and have messed around with it a bit in
my free time. Here are some things I'd want to know before trying it out on a
large project:

1\. What development environment are other developers using with very large
code bases? Is the tooling responsive?

2\. Using a language for a large project without some kind of async/await
notation seems painful. How bad is it using only combinators for async
(assuming you want to stay on stable)? And is there a date yet when
async/await will be stabilized?

3\. Most of the code I write is in F#. The nice thing is, there is basically
.NET library for everything under the sun. For example, over the weekend I was
looking for a library that could decode OBD-II data from a car, and with a
quick DuckDuckGo search I was able to find three or four. In a large-ish
project, how often do Rust users typically find themselves coming up short for
that kind of stuff? Obviously it varies a ton by domain, but maybe someone
could tell me whether the answer is "you'll probably write all wrappers for
3rd party APIs yourself" or "there's a decent chance there's a library out
there that you can adapt."

~~~
notheguyouthink
> Using a language for a large project without some kind of async/await
> notation seems painful. How bad is it using only combinators for async
> (assuming you want to stay on stable)? And is there a date yet when
> async/await will be stabilized?

Can someone explain to me what async/await is, in the context of a language
like Go? I've used Rust a fair amount, but Go has been my native language for
~5 years now.

With that said, aren't both languages synchronous languages where you can drop
into another thread (or green thread) whenever you want?

What UX is improved by async/await?

Eg, in Go I never felt the need for async/await (as I've used in JavaScript at
least). If anything, Go's is _better_ than async/await to me, because if I
call `foo := bar()`, I don't have to know if `bar()` is an asynchronous
function.. it just works. Is there something missing there?

Again, I use Rust and Go similarly in this post because Go is my frame of
reference. I _thought_ they were the same on this front, though. No?

What's missing here? What am I missing here?

~~~
steveklabnik
Yeah, you are missing some details. But this is a _huge_ area, and it's hard
to explain in a single HN comment. Basically, yes, the convenience of not
needing to write async/await is useful, but in order to accomplish that, you
need a bunch of other supporting decisions. These decisions make sense for Go,
but do not make sense for Rust.

The first thing to say on this topic is that virtually all of this (including
async/await) is a library in Rust, not a part of the language. You can build
whatever model you want on top. The one I'm describing is the futures/tokio
model, which is becoming the de-facto, default one. But if you want something
else, you can do it! This already is a divergence from Go, where you have what
the runtime gives you, and that's it.

Fundamentally, Go's model is cooperative coroutines, where the runtime does
the pre-emption for you. (You can also call it yourself but as you note, this
isn't usual.) It'll do this when you request something from the network, when
you sleep, or when you use a channel.

Tokio's model, on the other hand, is single-threaded. Instead of a ton of
gorutines, there's an event loop, and you place a chain of futures onto it.
Each chain of futures can sort of be thought of as a goroutine, but it's also
different. For example, Rust can know at compile time exactly how big each
futures' stack is, and so can do a single, exactly correct allocation. Anyway,
each future in the chain handles yielding for you.

Async/await, in Rust, is about making those chains easier to write.

Anyway, I hope that helps. Maybe someone else will have a good comparison too.
There's a _lot_ of options in this space, and similar terminology that means
the same, but slightly different things, so it can be hard to get your head
around.

~~~
Rusky
Technically you can get the convenience of not needing to write async/await,
with the same runtime implementation decisions underneath as Tokio/futures.
Kotlin is an example of this approach- its coroutines are state machines but
with implicit awaits.

This can even be extended to "awaiting across function calls" with effect
polymorphism. I haven't seen this done in this context, but it would allow
things like passing an async closure, or a generic type with an async trait
implementation, to a normal function and having it automatically become an
async function instead.

The real choice between explicit and implicit suspension points is thus
syntactic, not implementation-driven. The reasoning there is more along the
lines of "I like to see where my function might suspend" vs "I don't want to
pepper my code with `await`."

~~~
lmm
> I haven't seen this done in this context, but it would allow things like
> passing an async closure, or a generic type with an async trait
> implementation, to a normal function and having it automatically become an
> async function instead.

I've done that a fair bit in Scala using HKT. E.g. superclass is written in
terms of a generic monadic type, one subclass implementation uses Future
(actually EitherT with Future), another uses identity, a third uses Writer to
track statistics...

~~~
Rusky
Ah, right. I guess I have seen this sort of thing in Haskell then as well.
When implemented with HKTs it usually gets really messy with things like monad
transformers, so I prefer to think of asynchronicity more in terms of
continuations.

~~~
lmm
I find monad transformers much clearer than continuations; a monad transformer
stack tells you exactly how your effects are going to be interleaved, which is
necessarily complex, whereas a continuation could be doing anything at all,
the way I see it.

~~~
Rusky
No, continuations can be controlled similarly through effect polymorphism,
which is what I was trying to describe.

The difference is not that continuations allow anything at all, but that your
effects are commutative rather than layered like a monad transformer stack.

~~~
lmm
> continuations can be controlled similarly through effect polymorphism, which
> is what I was trying to describe.

Fair enough, but you're effectively talking about a secondary, novel type
system, right? One of the things I like about monads is that they can be just
plain old values of plain old datatypes.

> The difference is not that continuations allow anything at all, but that
> your effects are commutative rather than layered like a monad transformer
> stack.

Yeah, monad transformers do feel overly cumbersome for the cases where effects
do commute. The trouble is that some effects don't commute, and I've yet to
see a formalism that had a good solution to distinguishing effects that
commute from those that don't. (I read a paper about a tensor product
construct once, but couldn't really follow how it was supposed to work in
practice)

~~~
Rusky
> you're effectively talking about a secondary, novel type system, right?

Yes, exactly.

The main reason I prefer language-level effects rather than monads is for
composability with normal imperative control flow. Monads are written in terms
of higher order functions passing closures around, and that gets _really_
messy in a language where you not only have early return and loops with break
and continue, but you also have destructors that need to run as you enter and
leave scopes.

You can add that kind of stuff as monads but it gets _really_ messy, and is
basically completely untenable in a language like Rust that also cares about
memory management. Even if Rust did have HKT, it would still be impossible to
write a Monad trait that supports them all, for example.

This article makes some great points on the subject:
[http://blog.paralleluniverse.co/2015/08/07/scoped-
continuati...](http://blog.paralleluniverse.co/2015/08/07/scoped-
continuations/)

------
jahaja
I've come to realize that the main thing that is making me procrastinate
giving Rust a proper go is entirely superficial at this point. Basically it
looks like a "clever" programmers dream language - which is usually not my cup
of tea. I'm also worried that larger projects ("real-world" projects, so not
projects like Servo) will inevitably become an illegible mess when clever
programmers makes the most of a clever language.

That said, I plan to push through this admittedly superficial barrier in the
near future and hopefully calm this concern.

~~~
gridspy
May you always work with clever programmers who make clever code simple.

~~~
lossolo
Most "clever" programmers makes code a lot more complicated than it should be
to show how "clever" they are. The more complicated the language, the more
complicated code they will produce. I've seen this a lot of times.

~~~
johncf
At least some of those "clever" programmers will eventually become "wise"
enough to avoid doing that.

------
paulddraper
> while the Java server could use up to 5GB of RAM, the comparable Rust server
> only used 50MB

How...how is that possible? It just glosses over this interesting point. What
does "comparable" Java code do to use 100x more memory?

~~~
notriddle
Three things:

* Java tends to allocate things separately and manipulate pointers, while Rust allocates things inline. This introduces allocation overhead and eats up the space needed by the pointers themselves.

* Java uses a JIT, which consumes of memory for profiling state and for storing the resulting compiled code.

* A tracing garbage collector needs about five times as much memory as explicit freeing to reach the same performance. [https://en.wikipedia.org/wiki/Garbage_collection_(computer_s...](https://en.wikipedia.org/wiki/Garbage_collection_\(computer_science\)#Disadvantages)

* Like vegetarians, Rustaceans pay more attention to efficiency as a whole. This may give the appearance of Rust being more efficient, even if it's actually not.

~~~
Royalaid
Additionally things like Spring and dropwizard also add some overhead for
better developer UX which rust doesn't have yet.

~~~
cjalmeida
Spring is the new EJB. It's bloated and always amazes me on how complex it can
get when you need to implement something not provided by Spring itself.

With Java 9 modularity and lightweight libraries like Guice you can get much
nimbler apps.

------
jokoon
> Rust is much easier to teach than C or C++,

Not sure about that.

If you know C, which is easy to learn, you already know a lot about C++, or at
least the parts that are used most of the time.

Learning about the borrow checker, match enums don't seem very easy at first,
although they surely seem elegant and much safer.

~~~
steveklabnik
Many Rust programmers never learned C, or have only a cursory understanding of
it.

Most C++ people I talk to would strongly disagree that in C++, you should do
things the C way.

~~~
pjmlp
The C way in C++ should only be used the same way as unsafe in Rust.

Very well hidden in safe abstractions, and validated by a profiler that they
are actually better than the safer C++ alternatives.

------
senatorobama
This is some cringe PR buzzword-heavy marketing. Not what I expected on rust-
lang.org

------
jeff571
"Training existing Ruby developers to maintain safe C++ code would take too
much time."

Wow. Just wow.

------
tytytytytytytyt
Wow, that paper needs a tl;dr.

~~~
knowtheory
Really? For a 6 white paper?

Fine:

Tilde built a server monitoring daemon with Rust and it's low resource and
doesn't crash. Tilde thinks the Rust community & its resources make it easier
to teach to new team members.

~~~
tytytytytytytyt
Yes, really. It's not the length, it's the density of interesting content.

~~~
knowtheory
Yeah man, learning to scan a paper for interesting content is definitely a
skill worth developing!

~~~
tytytytytytytyt
I suggested it because it'd help other people not waste the time scanning the
paper for the same minuscule amount of not-that-interesting content. But at
least it gave you the opportunity to contribute your interesting comment.
Yeah, man!

