
The struggle with Rust - mpalme
https://ayende.com/blog/176801/the-struggle-with-rust?Key=ed64b64e-91c9-41be-ba0f-348641976e20
======
gnuvince
When I first learned OCaml (knowing only Python), I swore a lot at the type
checker for refusing to compile code that I knew would work at run-time (e.g.,
a variable having multiple types, but on strictly disjoint execution paths).
It seemed insane to me that people would want to subject themselves to this
kind of bondage and discipline. And yet, many years later now, I have learned
and internalized how type systems work, I have learned not to try and simply
replicate Python patterns in OCaml, and as a result I use OCaml to write code
quickly and without any headaches; and not only is the type checker no longer
an obstacle, it's now a valuable assistant.

I believe that we are seeing the same kind of phenomenon in Rust: we are
trying to replicate C or C++ code directly in Rust, and we get frustrated when
our efforts are foiled by the borrow checker. It's going to take some time and
some efforts before we learn and internalize how borrowing works in Rust and
how to change the way we write programs so that it is no longer an obstacle.
The Rust team looks very receptive to comments about taking away some pain
points (e.g., non-lexical lifetimes), but we also need to accept that, for a
little while, we are going to feel like we did when we were new programmers
and were trying to make sense of these new constraints.

~~~
tejinderss
Not related to the topic but how do you find ocaml as compared to python or
rust? Do you think its worth learning today as opposed to rust for high level
general purpose programming?

~~~
krat0sprakhar
Absolutely! If you're looking for fast, GC'd, native functional language I'd
say OCaml is a great option. There's actually a very comprehensive set of blog
posts migrating an open-source python based project to OCaml, that I'd
recommend reading if you're looking for a more thorough comparison -
[http://roscidus.com/blog/blog/2014/06/06/python-to-ocaml-
ret...](http://roscidus.com/blog/blog/2014/06/06/python-to-ocaml-
retrospective/)

Personally, I used OCaml while at university to write a programming language
([http://jsjs-lang.org](http://jsjs-lang.org)) and totally loved it. Real
world OCaml is a great resource and I'd definitely recommend giving OCaml a
try. My only regret is that I don't get to write OCaml as much as I want to
due to the kind of projects I work on, but given that Bucklescript[0] and
Reason[1] are moving ahead in full steam, that might change shortly too :)

[0]-
[https://bloomberg.github.io/bucklescript/](https://bloomberg.github.io/bucklescript/)

[1] - [https://facebook.github.io/reason/](https://facebook.github.io/reason/)

------
adwhit
These sorts of articles are starting to pop up a bit more frequently,
presumably because Rust is starting to get a bit of traction. The theme is "I
know $LOW_LEVEL_LANGUAGE therefore I should be able to program Rust. I spent a
few days and couldn't. I don't like Rust."

Unfortunately, things aren't that simple and Rust really is different from
other languages. It takes months of steady investment (and yes, frustration),
but the payoffs are spectacular. I would urge the author to persevere. In the
meantime, the community is very helpful and I'm sure you'll get lots of useful
advice on the back of this post.

~~~
rubber_duck
>It takes months of steady investment (and yes, frustration), but the payoffs
are spectacular.

This was the exact same thing I kept reading back when functional programming
was becoming in vogue - people were pushing for Haskell, saying that purity
and laziness were amazing, type system could almost prove correctness at
compile time, etc. etc. You _just_ need to meditate deeply on category theory
and draw direction graphs to become one with types and all would be revealed
to you.

Hybrid functional languages got adopted in the meantime (F#/Scala/Clojure come
to mind) and a lot of FP was adopted by OO languages - it wasn't pure or fancy
but it solved problems in an accessible way. Most people still don't use
Haskell.

I'm hoping Rust doesn't go down this path as I want a replacement for C++
badly. But saying "you need to spend a few months to get intimately acquainted
with the language" is just not going to work in practice if you're hoping
about breaking in to the mainstream.

~~~
crdoconnor
The clincher for me with respect to rust is that I see a steady stream of
useful software being developed in it.

I never really saw that in Haskell and I've heard people talking about it for
at least 15 years. That indicates that something is missing.

~~~
kazagistar
There is some pretty damn useful software written in haskell. For example, I
started using Pandoc long before I knew it was written in Haskell, and for me,
it still dwarfs the utility of any specific tool made in Rust. That said,
Haskell had a lot longer to get there, so Rust is still moving a lot faster on
that front.

------
Dowwie
I spent the last week working with Rust and learning from the Rustaceans. It's
been such a great experience. I am really glad that I dedicated some time to
it. In a short amount of time, I've already dramatically improved some
computationally expensive python code by more then 10x, largely with the help
of the community. Further, Rust is already making me a better programmer. If
you are skeptical, I challenge you to dedicate yourself completely to a solid
week crash course of Rust and experience this for yourself. If you struggle,
don't give up and blog about why you hate Rust. Join #rust-beginners on
irc.mozilla.org and ask for help.

------
omouse
_I know about GDB, that ain’t a good debugging experience_

I can't take this article seriously after reading that and after reading this:

 _So I spent a few evenings with Rust_

How long did it take you to learn C, C++, Java, Python, Ruby, Perl or other
languages? MONTHS. Many many many months. Sure you could get something up and
running in a few evenings but after many months did you realize that earlier
code could be better. Each language has its own obstacles and spending a few
evenings isn't enough time to get around them.

I gave Haskell a pass but after checking out Elm I can see more of its power.
I gave Rust a pass because the language was changing but now that it's stable
I expect to sit down for a few months at least learning it.

~~~
draw_down
I agree with him about GDB.

------
tps5
I'm not coming from C++, but my experience with Rust has been:

* Read the book, got some of the concepts but most of it went over my head.

* Did nothing, maybe thought about it a little bit.

* Two weeks later: read the book again. It made more sense but some of it is still lost on me.

* Actually wrote some Rust. Was hard, frequently consulted the book, the "by example" book, r/Rust, and google.

* Two weeks later, it's much easier. I'm still consulting outside resources frequently, but I generally know how to fix problems as they arise and my mental model of the how the language executes is becoming more and more accurate.

I've spent on average 30 minutes a day working on a pretty simple Rust program
for about a month now. The advantages of Rust so far have been: if it
compiles, it works. Extremely easy to refactor: for my main data structure,
I've switched between vecs and arrays back and forth probably 4 times to get a
feel for the differences. This is generally really easy to do because they
both implement the the iterator trait. In the other languages I use (at work),
switching between one type of collection and another would be a huge time sink
and involve a lot of annoying (though trivial) refactoring.

The main advantage of Rust for me (and the reason why I decided to check it
out vs go) is that I don't have a lot of experience thinking about memory at a
low level. The dynamic languages I work with abstract that away from the
programmer. I wanted something that would force me to think about how and
where memory is being allocated, and whether I want to pass by reference or
value. For someone who has spent a lot of time with C/C++ or just someone with
a background in CS/programming languages, this may all seem obvious to you.
But there's a lot of programmers out there who learned with dynamic languages
and don't have a good mental model for memory. Rust is a good instructional
tool for us.

I don't really have an opinion on whether Rust is "production ready." It's
being developed rapidly though, so I think it'll get there.

------
nercury
Yes, it is hard to use mutable shared values Rust. This is exactly the point.

~~~
creshal
So how do you avoid it?

~~~
nercury
Well, you either uniquely mutate, or keep the value immutable.

Usually you construct the pipeline to switch between these as you need. If you
need to share the result from one mutation to the other, you can do it by
passing a simple immutable value that communicates the state change to other
mutation, but don't try to do both at the same time. This is simply a good
practice enforced at compile time. Of course, sometimes these rules prevent
some valid cases, but overall it is worth it.

If we are talking about HashMaps, the Rust std lib has useful Entry api to for
get-or-insert use cases. In rare cases where you need to share the value
between different parts of the system, there are primitives you can wrap your
value in and enable reference counting, as well as internal mutation (even if
wrapper is immutable, it provides safe api for mutation). In cases where the
concurrency is needed, there are mutexes, channels, and multiple libraries to
parallelize the code (like rayon or crossbeam).

~~~
AstralStorm
The problem is there is nothing like "atomic mutable", equivalent of C++
std::atomic with at least sequential consistency.

So you get to drop all safety often instead to have shared mutability.

~~~
dbaupp
This is wrong. There is a std::sync::atomic module, along with types like
Mutex and RWLock.

------
drbawb
>We are talking about allocating memory, in a system level language, and
unless you are jumping through hops[sic], there is just no way to do that.

To be fair, if you want to call malloc it is still just as accessible[1][2] as
it is in C. If you search the Rust documentation for malloc `libc::malloc` is
even the first result. If you were to use that on stable Rust the error
message will even tell you to link your program to the libc published on
crates.io (which is just adding a line in your Cargo.toml file.)

I'm not sure I'd call that jumping through hoops. I'd love if Rust provided
easy access to its own allocator on the stable release channel; but I'm
willing to wait for them to get it right, these things take time.[3]

[1]: [https://doc.rust-lang.org/libc/x86_64-pc-windows-
msvc/libc/f...](https://doc.rust-lang.org/libc/x86_64-pc-windows-
msvc/libc/fn.malloc.html)

[2]: [https://is.gd/cgkX2R](https://is.gd/cgkX2R)

[3]:
[https://github.com/pnkfelix/rfcs/blob/117e5fca0988a1b0c4b6e4...](https://github.com/pnkfelix/rfcs/blob/117e5fca0988a1b0c4b6e4de83b633f92b0c9c84/text/0000-kinds-
of-allocators.md)

~~~
Manishearth
Also, most folks would use `Box` with `Box::into_raw` (also
`Vec::with_capacity` for dynamically sized stuff, which works fine.

RawVec is an implementation detail; Vec exposes most of what you need for
dealing with raw memory.

------
leshow
The first few of these types of posts were OK. But now it's a bit ridiculous.
You spent 2 days trying a new language and you didn't like it, your opinion
means nothing.

~~~
Etzos
I don't typically respond to comments like this anywhere, but I think this
kind of view is dangerous to have. And it's one the Rust community at large
seems to try to shy away from.

This kind of attitude towards people trying out a new language is damaging to
the person trying the new language and to people who are part of that
languages community. The former because it will give them the notion that the
community is hostile towards them and newcomers in general and the latter
because it can enforce a dogmatic view of the language ("you do it our way or
no way!" kind of feel).

A language's ability to have people pick it up easily is important. In Rust's
case there is definitely always going to be a bit of a hump for people to get
over, but the concern put forth in this article seems like a legitimate one,
but one the Rust community seems to be aware of. Acknowledging and helping
people with these problems (while searching for a way to potentially avoid
such a situation for the next newcomer) is important and it's critical that
you not simply dismiss someone's opinion because they don't know or understand
"the right way to do it" yet.

~~~
threatofrain
For a language like Rust that puts all the pain up-front, I want to hear
whether the pain is worth it. But someone who spends only a few days can't
give you a weighing of whether the pain is worth it.

I'm reluctant to even listen to someone's Amazon product opinion if they've
only used it 2 days.

~~~
Etzos
I certainly agree that an opinion piece like this has less weight than one
which comes from someone who has spent a considerable amount of time with a
language. However, saying that an opinion formed after only a few days is
"worthless" is taking it too far.

Plus if one completely ignores pieces like this then they are more likely to
fall into a rut where pain points like this are forgotten or ignored
completely. While the actual pain of getting used to the Rust way of doing
things may be harder to mitigate perhaps there is some insight in opinions
like the ones presented in the blog here which can lead to a way to help
people get over that pain in a quicker/less painful way.

------
protomikron
I do not know Rust, but I know C and some Haskell and AFAIK one should
building a program in Rust differently than in C/C++ (which is probably the
authors background).

Get out a quick _correct_ (and this can not be emphasized enough) version
(obviously one has to learn the language here) and _then_ optimize (when you
have numbers). The author did not seem to get a working version with Rust
(sorry, if I am wrong on this), and tried an approach that he knows would work
for C/C++. But maybe another approach would have been already performant
enough?

------
pron
Rust provides something that few if any other languages do: memory safety
without GC. It is very obvious that this comes at a non-negligible cost in
development effort. If you need what Rust provides -- and an important class
of software certainly does -- you should be more than happy to pay that extra
cost. If you don't, there are plenty of alternatives. But exchanging effort
for rather unique guarantees is the very explicit tradeoff that Rust makes.
That few other languages choose to make this tradeoff is what makes Rust so
valuable.

~~~
burntsushi
To be clear, I think the data supports that Rust comes with a non-negligible
_learning_ cost. Whether that cost is continually paid isn't clear yet. (I
personally think the answer is "no.")

~~~
pron
I agree that there's no data, but I would be very surprised if the answer is
indeed no, as there are at least good theoretical reasons to assume extra
cost. Proving anything (let alone to a compiler) comes at a computational
complexity cost (think of the proof as a certificate). In principle, a large
number of proofs may be "free" in the sense that no more information than the
programmer normally supplies the compiler is necessary. But if this is true in
_every_ case, that is very surprising.

~~~
burntsushi
It's not very surprising to me. Paraphrasing, "once I got past the initial
hurdle of the borrow checker, programming in Rust became much easier," is
frequently uttered in the Rust community.

I'm not trying to make a very nuanced claim here. All I'm saying is that
there's clearly a lot of people who struggle while learning Rust, but that
this is very different than paying an eternal development cost similar to that
struggle. We should be careful not to conflate them.

(I did not meant to get into a broader philosophical discussion about using a
type system. That's a different conversation IMO, and I disagree with your
analysis because you only focus on the upfront cost while neglecting the
maintenance costs in the absence of such proofs.)

~~~
pron
> programming in Rust became much easier

Much easier than what? Writing C? I believe you.

I agree with you about maintenance, and I agree that there are two separate
issues, but still, extra proof must invariably carry some extra mental burden.
There are no free proofs just as there are no free prime factors. This isn't a
philosophical issue, but a computational one. But it's also true that the
mental burden might offset a different sort of mental burden. So whether there
is additional work or not is unclear, but I think it's pretty clear that the
_nature_ of the work is different.

~~~
burntsushi
I don't disagree. But I find this to be a very different claim than the one I
initially protested. To re-iterate: I'm trying to point out that there's a
difference between learning costs and eternal development costs. The OP is a
data point in support of the former, not the latter.

I'm not going to debate the trade offs of type systems with you. It's been
done to death.

~~~
pron
I doubt there would be much debate, as we're likely on the same side :)

------
jeffdavis
Writing libraries, especially implementations of well-known algorithms and
data structures, is a bad way to learn most languages. Technically, it's
possible, but I think it skews the experience strongly toward languages that
are similar to what you've worked with before and languages that are more
permissive.

I suggest writing applications, or libraries closer to applications (e.g. a
specific library that parses a special data format important to your domain),
where you have a clear goal but not a clear path to the goal in mind. In other
words, hacking.

Let's say the author had finished the Trie. What good experience could he
possibly have? It's not going to magically run faster than the C++ version or
use less memory. It's a well-known algorithm so there probably aren't subtle
bugs in the C++ version that rust would have caught. The best you could
possibly say is that it was faster to write -- which is exactly why it skews
the experience toward familiar and permissive languages.

I am trying to learn rust (albeit very slowly) by doing some hacking to make
it work better for postgres extensions. I know postgres, and I think it would
be cool if it were easier to write postgres extensions in rust. Of course you
can today, but you'd have to use a lot of unsafe FFI calls.

------
eximius
If you just want to allocate a chunk of memory for random use like you would
in C, it can't be guaranteed to be safe. So I would _hope_ you need to jump
through hoops.

~~~
AstralStorm
It is a rust weakness that it does not have "partial unsafe" that guarantees
_some_ things. Makes many kinds of code unnecessarily hard to reason about.

------
gravypod
Could the issues of rust be rectified with a better theorem proved that goes
past "2 things are touching this"? I don't think I'd mind if the compiler said
"your code will not work because eventually it will modify the same bytes at
the same time and create an unpredictable state" but I will mind if it says
"your not good because you have two mutable references.

~~~
Diggsey
Having a more powerful theorem prover is not always better: it makes it harder
for the programmer to know whether a piece of code will work, and you can
start getting non-local effects, where a change in one part of the code can
break a (seemingly) unrelated part.

However, what you're asking for is very similar to what types like `RefCell`
and `RwLock` do - they effectively delay borrow-checking until runtime. Now
you can have references to a `RefCell` in multiple places in the code, and
they can all get mutable access to the interior, just not at the same time.

Having said that, I find it interesting how rarely things like `RefCell` are
needed in practice: there's almost always a better way to satisfy the borrow
checker, and it usually results in much cleaner code.

~~~
gravypod
> Having a more powerful theorem prover is not always better: it makes it
> harder for the programmer to know whether a piece of code will work, and you
> can start getting non-local effects, where a change in one part of the code
> can break a (seemingly) unrelated part.

I think there are two solutions to that problem. The first is to involve HCI
people in the development in the language to see how they can improve the
output and communication from the theorm prover to the programmer. I imagine
that, some time in the future, a very powerful therom prover will make its way
into a "general" language and will bring some cool effects. The theorum prover
I'd like to see will attempt to prove all of the conjectures used in the
program and, when not possible, will say "possibly invalid code. Please solve
this conjecture" where then this would be given to a mathamatician and they
have to complete the missing steps to see if it works, if not we tell the
compiler this isn't going to work and it now generates errors for this.

The other solution is to limit the shared state of every program using very
strict segregation of all moving parts and an attempt to limit use of side-
effect-causing operations.

This second one is nothing new, I mean the original LISP crowd had these ideas
down and theres nothing new.

I don't think I've ever worked with a compiler that has a built in theorum
prover, possibly a shared library of proven work, and a way to in the language
aid the prover. For instance this type of function definition

    
    
        int(int numerator, int denominator) where (denominator != 0) divide {
            return numerator / denominator
        }
    

And even more so I've not heard of unobtrusive theorum provers that keep out
of the programmers way while still being useful. I think an unproven relation
should be a warning. It's not good but the program can still run and you (you
being not me but the math-people) can come up after me and prove all of my
work or tell me I'm an idiot and to fix it.

~~~
junke
This example works in SBCL, where types (possible disjoint sets of values) are
treated as assertions. By default I always set "safety" and "debug" to 3 (the
max), which helps.

    
    
        ;; Giving names to abstract things a little
        (deftype zero () `(eql 0))
        (deftype non-zero () `(and number (not zero)))
    
        ;; Declaration
        (declaim (ftype (function (number non-zero) number) divide))
    
        ;; Definition
        (defun divide (n d) (/ n d))
    
        ;; Test
        (lambda (u v)
          ;; introduce a type constraint here.
          (declare (type zero v))
          (divide u v)) ;; (nb: underlined in orange)
    

This message is also printed:

    
    
        note: deleting unreachable code
        warning: 
          Derived type of V is
            (VALUES (INTEGER 0 0) &OPTIONAL),
          conflicting with its asserted type
            (OR (AND NUMBER (NOT INTEGER)) (INTEGER * -1) (INTEGER 1)).
          See also:
            SBCL Manual, Handling of Types [:node]
    

The warning happens when two types are and-ed (intersection), and the
resulting set is empty. You still allow code where there is potentially "bad"
value flowing from one place to another, because ultimately you know the
runtime will detect it. In other words, the goal is to reject false negatives
(unwarranted warnings), which contrasts with statically typed languages where
you only accept code that is guaranteed to satisfy static typing rules (it
makes sense there because the compiler is your last chance of catching those
type errors, since the runtime does generally not perform type checks).

(edit: actually use non-zero instead of (not zero))

~~~
gravypod
Yes that is exactly what I'd like but I'd like something that's grown past
just type checking. There are many more bugs that I know can be caught by a
theorem prover.

Edit: I want to clarify. I think just type checking should be written as "Just
type checking".

~~~
AstralStorm
Indeed, something like what is being tried in Pyret to make contracts (a kind
of theorem lemma) the first class citizen.

------
kernelbandwidth
> The very same trie code that I tried to write in Rust I wrote in one & half
> evenings in C++

Use unsafe. No, really, just use unsafe.

Clearly a big value of Rust is that it's a safe language, and the design
patterns around safety and affine typing are emerging and good. We should want
people to write and use safe code as much as possible. But the trie example
seems, to me, to be very much the sort of thing unsafe is for. Writing in C++
is essentially doing everything in unsafe. Unsafe is not evil; it's an
important part of the language and when it's called for, it's called for.

~~~
Manishearth
I'm not sure, tries are pretty easy with safe code. It's only things like data
structures with parent pointers where things get hairy.

If your goal is to use malloc and stuff (which seems to be what the author
wanted) then of course you should use unsafe. Unsafe is indeed for doing
abstraction-writing like this. Still, you should avoid it if you can.

------
vbezhenar
I don't know Rust good enough, but do unsafe blocks help there? Data
structures belong to library code and libraries surely can utilize unsafe
blocks, because they are expected to be heavily tested.

------
nimmer
Sounds like Nim is what you are looking for: [http://nim-
lang.org/](http://nim-lang.org/)

It's fast, has a Python-like syntas, a good degree of memory safety and
optional GC.

------
w8rbt
___" multiple mutable references"_ __... I would use C++ for this. I would not
use rust if I had a design that required that ability.

------
grabcocque
These kinds of articles are always hostages to fortune. Because I guarantee
somebody, any second now, will demonstrate that what the author wanted to do
was trivial, and if he'd spent a bit less time trying to start flamewars on
Hacker News he'd have discovered it.

Not me, by the way, I don't know rust. But I reckon it'll be about three
comments down.

~~~
Amezarak
I think part of the problem is this: if you're say, a C++ programmer, you can
write C++ in most other languages and have a working program, even if it's not
idiomatic.

You can't in, say, SQL, Rust, or Lisp. That doesn't make them _a priori_
harder than other languages, it just requires a shift in thinking, whereas if
you're jumping to say, Java after C++, you can get started right away and
hopefully pick up idiomatic Java as you go along.

Instead of thinking "here's how I would implement this in C++, now how do I
translate that to Rust?" you have to cut that part out and think "how do I
write this in Rust?" I mentioned SQL because that's where that really, really,
clicked for me: if you ever write SQL and think imperatively, you are _doing
it wrong_ , you have to think in _sets_ , and once you do that, SQL is the
easiest and most natural thing in the world. The Rust Book is pretty good, but
I think it would really benefit from an in-depth section early on explaining
how writing in Rust requires a paradigm shift in your thinking, and exactly
how to think Rust-ically. I'd write something here, but I'm still learning
how. :)

~~~
steveklabnik
We're re-writing the Rust book at the moment: [http://rust-
lang.github.io/book/](http://rust-lang.github.io/book/)

It doesn't explicitly have a section like this, exactly, but there are several
"thinking in Rust" style chapters towards the end.

------
justinlaster
>So I spent a few evenings with Rust

I stopped reading. There is literally nothing you could possibly say that'd be
worthwhile after "a few evenings" with Rust, especially when you lack the
proper background in the first place.

This trend of "The problem with Rust: A naive and inexperienced
perspective"-esque articles is already old.

~~~
bronson
I made it to

> I have a buffer, that I want to mutate using pointers

Turns out you were right.

~~~
gcp
Yeah, that entire paragraph is basically a list of all the things safe Rust
will not allow you to do. It should be a sign that you're tackling the problem
the wrong way around...

------
moomin
The joke being, this is being written by a guy who has constantly insisted on
his blog that if people don't think NHibernate and IoC are a good idea for
their project, it's because of their personal failings.

Me, I muck around with Haskell. No I'm not productive in it. I don't think
that's Haskell's problem, though.

