
Why is Rust difficult? - chewbacha
https://vorner.github.io/difficult.html
======
jchw
I like Rust so far, but there's a few things I think aren't true:

* That Rust is only harder because it enforces 'correctness.' It certainly is harder because it enforces correctness, but it's also harder because of _how_. I'm not saying there's a better approach to this, but I think a lot of people are implying that there isn't, and I don't think that's a safe assumption. I think that we could find ways to make equally memory-safe languages that go about enforcing safety in entirely different manners than with ownership and lifetime semantics.

* In fact, the entire idea that Rust enforces correctness. Only if your definition of 'correctness' to be memory-safety, but I would normally define 'correctness' to include rigorous mathematical proofs. Rust's safety guarantees are often accidentally blown out of proportion; they mainly aid in preventing security and concurrency bugs, but only a certain class of each. This is still useful, but this caveat really needs to be in your face more often, as a lot of people will not mention it when touting the benefits of Rust, and beginners can get easily confused about what exactly Rust prevents you from doing.

* The idea that Rust's approach is always worth the trade-offs. Go is another programming language I like, and there are definitely things that are simply easier to write in Go with few disadvantages. Fearless concurrency is a wonderful feature, but for embarrassingly parallel problems like, often, web servers, where each thread is usually independent in terms of mutable state, Go works wonderfully. It also lets you shoot yourself in the foot in a way that Rust wouldn't, but often for a lot of simpler apps it still ends up being easier.

* The idea that solving the compiler errors makes you understand the problems correctly. For example, you could always just clone memory at every occasion, return the input instead of borrowing, etc. In fact, these things might be easier for a beginner to do. There will probably be a ton of Rust anti-patterns that come about from trying to resolve compiler errors.

~~~
josephg
> Go is another programming language I like, ... Fearless concurrency is a
> wonderful feature, but for embarrassingly parallel problems Go works
> wonderfully.

I adore Go's concurrency model but loathe go's actual language. The constant
repetition in error handling, lack of generics and lack of parameterised types
and Option<> make it feel like a children's toy set version of C instead of a
useful modern language akin to Rust and Swift (and modern javascript).

I really really love the concurrency model though. And as far as I can tell
there isn't much like it available elsewhere. You can make a similar
concurrency model in rust, but you have to use much heavier OS level threads
to do it, and all the other crates don't support it. Erlang / elixir do it but
come with a much higher runtime performance penalty. Pony is interesting but
very new - there aren't a lot of libraries for it and when I was playing with
it the compiler seemed crazy slow.

I'm a little bit tempted to make a simple compile-to-go language. I'd get
lynched at Go meetups for fragmenting the ecosystem, but it might be worth it.
I like almost everything about go except the language itself.

~~~
unethical_ban
I write somewhat simple programs and webapps for my job, from time to time. I
use Python and its standard library, some modules, and the Bottle Framework.
Pulling data from APIs, doing analysis, taking some user input, editing
configs, etc.

I hardly ever use classes unless I'm extending a vendor library. I have never
used generics. Why are generics such a critical component of a programming
language that every thread about Go mentions it? It's an honest question from
me.

~~~
rspeele
Python is a dynamically typed language so when you say you've never used
generics, that makes sense -- the concept does not apply to dynamically typed
languages. But I bet you frequently use lists and dictionaries, and perhaps
occasionally use higher-order functions like map, filter, and reduce. All of
those would be generics in a typical statically typed language, because with
static typing you don't just have "a list" but "a list of ints" or "a list of
strings".

Go has built-in generic arrays, slices, and dictionaries, but nothing else,
and you can't define your own functions that work on those without specifying
the type. So say you write a function that shuffles the elements of an array,
like Python's random.shuffle(list). You can't make your function work for all
kinds of arrays. You have to have a different function to shuffle arrays of
strings, arrays of ints, arrays of FooBarClass, etc. even though the shuffling
logic doesn't give a damn about what sort of thing is in the array.

I have a suspicion that this _occasionally_ leads developers to write code
inline that they previously would've extracted into a utility function. See
the answers here:

[https://stackoverflow.com/questions/12264789/shuffle-
array-i...](https://stackoverflow.com/questions/12264789/shuffle-array-in-go)

~~~
unethical_ban
Thanks. I took a year of CS ten years ago and haven't had to do statically
typed development of any size since then - certainly not developing libraries.

So essentially, containers and function overloading is damned near impossible.
Got it.

------
cortesoft
I do think a lot of the difficulty when starting Rust was all about it forcing
you to do everything correctly, even when the 'correctness' wasn't needed for
how the program was currently being used.

However, my experience learning Rust was that it was pretty easy to work
through these things because of how precise the compiler is at describing the
issue. I feel like the compiler was my teacher, and it kept on teaching me new
concepts to look up in the docs. It was a grind to learn, but not a
'difficult' grind in the sense of getting stuck and banging my head against
the same thing for a long time.

~~~
bonesss
> I feel like the compiler was my teacher, and it kept on teaching me new
> concepts to look up in the docs.

I work primarily in F#, a functional .Net language, which also enforces strict
rules at compile time to ensure correctness... With the benefit of experience
it's _amazing_ and _comforting_ and lets you reason happily about applications
at a higher level. It's on of my favorite parts of the language, TBH.

And while I see its strength over time, and see the benefit it gives to code
bases, I _also_ know that for your average C# dev used to firing up their app
and debugging at run-time, or working in certain ways with their code, it
feels like putting on a straight-jacket. It hurts the "first week" story to
the benefit of long term concerns.

I totally agree: it's forced learning, which makes it a bit of a grind. But
damned if I'm not a much better programmer for the effort :)

~~~
pjmlp
F# is nice, if only Microsoft would actually provide feature parity with C#,
VB.NET and C++ tools.

I bet C# will sooner copy F# features than Blend, GUI designers or .NET Native
will support F#.

~~~
bonesss
That response is kinda offtopic for Rust... but:

F# is a first class .Net language that interops rather well with the others...
If you have some burning need to use Blend or a GUI designer then you can
always have a C# shim feeding into an F# domain/core with little problem. It's
the same interop story as VB.Net, fundamentally.

That said: if you track MS's track-record those technologies and their
Enterprise market viability, outside of resume-driven-development there isn't
a lot of reason to be throwing hard money at their new shiney shiney... C#
lags drastically behind F# in areas not focused on the client: scientific
computing, cloud computing, parallel computing, agent/actor systems, etc...
Being a release cycle off on the coolest new drag-n-drop tooling from MS is a
moderating influence that has created a _much better_ cross-platform story,
and OSS story, for F# than C# has ever achieved, despite the scale difference.

Oh, and C# _has_ been actively trying to copy F# for many release cycles,
including reworking basic language design decisions... It is, however,
fundamentally unable to achieve that goal based on the languages overriding
design. I'm impressed they're back-working non-nullable value objects, for
example, but the language can't give _guarantees_ that ML languages can.
Pattern matching is nice, but exhaustive pattern matching is what you want for
hard things, IoW. And those deficiencies make C# a non-starter or also-ran for
a lot of the BigData, streaming, cross-platform systems you see in big data
halls and scientific computing.

It's almost like different tools are good for different things ;)

------
hsivonen
It's a good article, but touches on a pet peeve on mine:

It bothers me when people who have written C or C++ mention lifetimes as a
novel _concept_. What's novel is that in Rust the compiler cares about
lifetimes. As a _concept_ , though, lifetimes aren't novel and thinking about
lifetimes _conceptually_ is essential to writing C or C++ that's correct
enough that it can be exposed to untrusted input. It's great that the Rust
compiler relieves this cognitive load on the programmer compared to C and C++
leaving it up to the programmer to take care of.

~~~
jstimpfle
Yes. And tackling vague conceptual problems by introducing a uniform mechanism
and syntax leads to overcomplicated designs. That's from my experience using
C++ and other languages, at least -- Rust has always intimidated me so I
haven't tried it.

Many problems are actually not that hard if we start only with the truly given
things. Namely at C level, or even assembler level. For example, I recently
had the opportunity to convert a design based on OS threads to one employing
userspace threads ("cooperative multithreading"). It was a big success, since
the need for synchronisation primitives completely went away, and it was much
easier to program than e.g. Javascript -- which requires callback chaining.
Any language that can't give as good control as C does needs to build huge
infrastructure to support approaches like that (if it at all suits the
language's semantics). And strictly, even C is too much abstraction - to
support green threads some non-portable parts are needed.

------
k__
Because it forces you to handle edge cases.

Java is also harder than JavaScript because of this.

Yes, the edge cases Rust forces you to handle are different than those of Java
and probably make more sense to solve, but I think this is still the root of
the "problem".

While edge cases are part of "the rule" they feel like exceptions of it.

Most of the time you write 80% of the program code in 20% of the time and the
remaining 20% eats 80% of the time because of the edge cases.

In many cases you can simply ignore the edge cases, because well they will
fail, but nobody cares 90% of the time. If the remaining 10% make too much
problems, invest some time in workarounds, but 10% of the remaining 20% isn't
that much, so this often will cost you also just 10% of the 80% of time.

If you can't ignore edge cases in your software, because even a 2% chance of
people getting injured is a huge problem, you use Rust and fight with these
cases.

~~~
wasted_intel
You can also opt to skip handling these edge cases by either copying
(decreasing performance) or unwrapping (decreasing reliability). The great
thing is that Rust gives you the choice.

------
stmw
Good article, although there are also some unforced errors of what Rust
community calls "ergonomics" \- many of which have been addressed, and
hopefully many others will be soon - that make things more difficult to learn
than the absolutely have to be. But author is quite correct that some things
are just hard, and if a language is being "honest", it must expose them to the
user.

~~~
tatterdemalion
What we could do better than we do today - and your comment about ergonomics
alludes to our work to improve this - is ease the onboarding of that
complexity we have to be "honest" about. Its a design constraint of Rust that
it must maximize user control, but that does not imply that users have to be
faced with all of those choices as soon as they first try to write Rust. In
some respects I think this article is an attempt at a counterargument to that
work (suggesting that it is "dishonest") but I fundamentally do not believe
that there is a contradiction between giving advanced users control and making
it easier for new users to write correct code before they have a full
understanding of the entire system.

~~~
majewsky
I would like a language that has knobs (e.g. file-level pragmas) for
strictness, which you could turn all to one side (to get something as strict
as Rust) or all the way to the other side (to get something like Ruby) or
somewhere in between.

For example, a REPL would be pretty non-strict: When you define a function,
you don't have to annotate types on the arguments, and everything gets passed
around as generic objects. However, the standard library would be pretty
strict [1], so in your unstrict REPL code, types are checked (and implicit
clones are made to satisfy the borrow checker) as soon as you call into the
strict code.

[1] In fact, the package repository for this hypothetical language (the analog
to crates.io etc.) should only accept strict code, or alternatively put HUGE
warning signs on libraries that contain unstrict code.

~~~
kqr
For the record, in PL jargon, "strictness" commonly refers to how soon the
arguments in a function call are to be scheduled for evaluation. Using it to
talk about the extent of static correctness checks can be slightly confusing!

------
sjroot
I opened this link expecting someone to complain about the difficulty,
verbosity, or other issues commonly mentioned by those learning Rust. I was
pleasantly surprised!

I learned Rust by implementing an interpreter for one of my graduate classes.
I really enjoyed it, especially their compiler which continues to become even
more awesome every day.

If you want to get into systems level programming, or already work at that
level, you owe it to yourself to explore this as an option.

------
jadbox
I feel Go and Rust are great options for writing modern server side
applications today, but they exist on two different ends of the spectrum. The
Go language is highly minimal, constrained, and forces devopers to write
unified code, that performs extremely fast idiomatically. This uniformity is
helpful for open source collaboration. Rust is a much more robust language (eg
generics), but is more complicated to pick up and harder to read by
comparison. Imho, Go is ideal for general web services, while Rust is better
for system apps, tools, or complex web services like real-time exchanges or
financial systems. This is just a general guideline though in my mind.

~~~
fpoling
Go does not perform "extremely fast idiomatically". There are a lot of cases
where replacing channels with semaphores or atomics, avoiding too many go
routines etc. can lead to significantly better performance.

But Go does perform good enough if one uses simple idiomatic code style while
making things relatively safe and maintainable. That is its huge advantage.

------
krapht
One of these days I'll get around to Rust. I don't think it's fundamentally
difficult, just different from most people's cowboy programming. I'm much more
excited these days about program extraction from verified programs written in
Idris or Coq, which is on a whole other level of pain and suffering.

~~~
bitL
Mind you even correctness checkers aren't panacea - they won't go all the way
down to check if you are using IEEE 754 correctly and handle all possible edge
cases and rather assume "well-defined math properties". And as Donald Knuth
used to say: "Beware of bugs in the above code; I have only proved it correct,
not tried it."

~~~
jacobush
Made me think, maybe they _should_ check if you are using IEEE 754
correctly...

~~~
TheDong
Numeric 'correctness' is a business-problem property.

For a game engine, correctness is speed and "right enough". For a research
physics simulation, it may be "as right as possible".

You can't check whether floating point inaccuracy is correct generically
because the definition is entirely dependent on the business needs of the
program in question.

~~~
jononor
One could enforce the strictest correctness by default, and then allow/require
to opt-out in cases where programmer decides it is OK, like a game.

------
dolguldur
Will Swift ever be able to span all the way down to enable Rust-like
performance in places where it’s necessary?

Or is it much more that it takes for a language to be a viable choice for
starting new C++ type of projects with?

~~~
smitherfield
To (potentially) match Rust/C++ performance Swift needs

1\. Stack and static-allocated arrays.

2\. True pass-by-reference for value types.

3\. A sufficiently-smarter compiler that's more often able to elide dynamic
allocations, reference counting, bounds checks, vtables etc. (Lifetime
semantics will help with this).

------
gkya
A minor problem with Rust is aesthetics. The syntax might look cryptic at
times, and error messages the compiler outputs are not everybody's cup of tea,
I'd guess. These are certainly not all that important when you're considering
it for the actual benefits of the language, but otherwise that is a little con
for me. If you write:

    
    
      println!("Hello, {what}!", "world");
    

in your program, the error message you get is 16 lines long. And there seems
to be no option to have terser error output.

------
vatotemking
Rust error handling along with the From trait is so good. I've been trying to
replicate a sort of similar behavior in nodejs but its not as ergonomic.

------
kumarvvr
After years of using Python, it's been difficult to wrap my head around GO and
Rust.

I really wish there was a course on Rust / Go for python programmers.

~~~
narimiran
> _After years of using Python, it 's been difficult to wrap my head around GO
> and Rust._

I had the similar post-Python problem, until I settled on Nim [0] - it has
Python-like syntax, significant whitespace, and ease of writing (but don't
expect Python level of it), while compiling to C and providing significant
speed benefits.

Also there is a brief "Nim for Python Programmers" [1], which might interest
you.

[0] [https://nim-lang.org/](https://nim-lang.org/)

[1] [https://github.com/nim-lang/Nim/wiki/Nim-for-Python-
Programm...](https://github.com/nim-lang/Nim/wiki/Nim-for-Python-Programmers)

~~~
joshbaptiste
Ditto.. Nim was just easier for me to absorb as a Python person myself and I'm
really enjoying it.

------
singularity2001
(for newcomers) nothing works out of the box or as expected

program[0]="+"; | ^^^^^^^^^^ the type `str` cannot be mutably indexed by
`{integer}` error: use of unstable library feature 'collections': needs
investigation to see if to_string() can match perf

error: borrowed value does not live long enough reference must be valid for
the block suffix following statement 0 When you use an index operator ([]) you
get the actual object at index location. You do not get a reference, pointer
or copy. cannot move out of indexed content

~~~
pornel
Rust is especially difficult if you bring your C-like assumptions to it.

You first need to learn difference between a borrow (a view, which may be
read-only) and owned type, and know that Rust enforces stdlib strings to be
UTF-8. Random mutation of a byte in a string is possible, but it has been
deliberately put behind an unsafe function call, and there are better
alternatives available (like char iterators).

It's not hard once you "get" the way Rust works with data.

~~~
alkonaut
> Rust is especially difficult if you bring your C-like assumptions to it.

To be fair it's very difficult if you bring your JS/Python/C#/Java assumptions
there too. As a lifelong Java/C# dev, I just never imagined having _objects_
on the stack. In C# knowing what goes on the stack is important - but the
thing with everything stack is that it's _copied_ \- so it's basically just
trivial values that sit on the stack. In Rust and C++ you can make entire
complex applications whose data is entirely on the stack. And getting over
that treshold is a big mental barrier.

I'm used to just thinking of my app state as a blob on the heap. A functional
programmer or a C++ programmer thinks differently.

------
resource0x
could someone point to the article explaining how Rust compiler reasons about
the program, how different kinds of objects and pointers are represented in
runtime - in a word, something that would help me grasp what is really going
on when I compile and run the program. The tutorials I saw are written in
assumption that you don't need to know that. Which makes me feel like a total
idiot

~~~
pzone
The introductory manual "The Rust Programming Language" is a good place to get
started. [https://doc.rust-lang.org/book/second-edition/](https://doc.rust-
lang.org/book/second-edition/)

------
bitL
Isn't the problem with Rust that it uses crappy pessimistic heuristics (borrow
checker) for essentially NP-C problem, causing complaints where code is
perfectly OK? IMO interesting design decision but I am staying away precisely
because of this.

~~~
ohazi
"Crappy pessimistic heuristics"

Are you talking about the borrow checker from half a decade ago? [0] Have you
used the language since then? This is FUD -- it's really not hard to get code
past the borrow checker anymore. Maybe every thousand lines or so you'll need
to add a block or pull a temporary variable out of a one-liner, but the
compiler basically tells you exactly what to do when something like that is
relevant.

[0] [https://pcwalton.github.io/blog/2013/01/21/the-new-borrow-
ch...](https://pcwalton.github.io/blog/2013/01/21/the-new-borrow-check-in-a-
nutshell/)

~~~
onli
Oh, I'd love if that were true. But in my limited experience with Rust it was
still the borrow checker that absolutely made writing in the language hard.
That was in 2017. My mental model of what should be safe was not at all the
model the borrow checker had. And I really read the documentation about that
part of the language.

I guess a confirmation that there were problems there is that they tried to
improve the borrow checker in the releases after that.

~~~
smaddox
Were you writing functions/procedures under the implicit assumption that they
would be executed under a single threaded context? Because Rust does not allow
this assumption without using "unsafe". I've run into this before, and it can
be a bit irritating, but if there's even a small chance you'll later want to
execute the code in a multithreaded context, it will more than pay for the
inconvenience. And if you're sure you won't, then you can just throw the data
into an UnsafeCell and call it a day.

~~~
steveklabnik
It's not even inherently about multi-threading
[https://manishearth.github.io/blog/2015/05/17/the-problem-
wi...](https://manishearth.github.io/blog/2015/05/17/the-problem-with-shared-
mutability/)

------
dumindunuwan
What you guys think about a documentation like on [https://learning-
rust.github.io](https://learning-rust.github.io) . Will it be difficult to
learn Rust with this type of documentation?

------
_pmf_
It's similar to learning Stateflow: it has a bigger upfront cost in program
design, but once it's fully specified, the implementation will likely be
correct (less degrees of freedom).

------
Animats
Rust's problem is not correctness. The borrow checker is the good part of the
language. It's feeping creaturism. The language started out as imperative, and
then became semi-functional. It started out as thread-oriented, and now is
acquiring "green threads"/coroutines/cooperative multitasking. The generics
system and library started where C++ and Boost left off. The "trait" system
was more "objects bad, must do something else" than an improved idea.

The Go guys knew when to stop. The Rust guys don't.

~~~
Manishearth
Er, no, Rust started out pretty solidly as a functional language.

It used to look like an ML variant, with purity and stuff. And it had a GC.
It's lost most of that now.

It also used to have green threads up till 1.0 (they were removed pre-1.0),
we're now adding them _back_. And they're being added back as a library, not
as part of the language.

It moved away from both of these.

------
sillysaurus3
Is there a way to avoid the true-believer syndrome for Rust?

I want to embrace Rust, but everyone ~100% of the time comes away chanting
about how awesome Rust is. So much so that it's a bit unsettling. Zealotry in
general is bad, but especially in programming: once you identify as an X
programmer, you lose out on ideas from Y and Z.

Every tool has its flaws, but for whatever reason it seems extremely rare to
discuss Rust's. When you try, it's like you're the Pied Piper: your song
brings seemingly the entire community scurrying towards you, ready to force-
push you why you're wrong and why Rust is awesome.

How about no REPL? We Go now. Slow compiler? Check. Performance is nice, and
so is parallelism, but it costs static typing. It's a nice tool to have in
your pocket as a fallback, but where's the niche? The ecosystem is huge, too:
run `du -hs ~/.cargo/` and see how many GBs you're losing. This is a really
tough sell on a 256GB SSD.

"So get a bigger SSD!" "Yeah but the compiler is always improving" "Yes but
real programmers don't really need a REPL" "Yeah but static typing is worth
the tradeoffs"

These refrains have been identical for nearly a year. Is there anything new to
say, or is it time to agree to disagree?

~~~
titanix2
This is the exact reason I won’t learn Rust past looking quickly the doc: the
promotion made by some very vocal people is a huge turn off. Notably by saying
that everything must be rewritten in Rust, it implies that every other
language is sh*t and people using them are dumb. Of course I don’t like that
view for my work nor I won’t to be associated with this kind of people. This
is a bit sad because the language probably have some interesting points, but
the social aspect of it just don’t fit the bill.

~~~
steveklabnik
Where are you seeing these kinds of comments? Can you link me to them? I’d
like to tell them to cut it out.

~~~
frankzinger
It's calmed down the past year or so but before that it was so insane
(especially here on HN) that the impression will be hard to erase for those
who were around to see it.

~~~
steveklabnik
I was around, but still didn't actually see it. I see people complaining about
it more than I actually see the comments themselves. That's why I always ask
for concrete examples, and people never actually show them to me.

