
The Rust borrow checker from a different perspective - utaal
https://blog.systems.ethz.ch/blog/2018/a-hammer-you-can-only-hold-by-the-handle.html
======
wilkystyle
As someone who is not familiar with Rust at all, wow—what refreshingly helpful
error messages!

~~~
krackers
Can someone chime in as to why C++ compilers in particular are notorious for
extremely verbose yet unhelpful error messages? Is it something endemic to
parsing the language that there can't be errors as helpful as Rust's?

~~~
oddity
The short answer, devoid of some interesting nuance, is that C++’s template
system is similar to an untyped functional programming language, so the errors
you get are the compiler’s attempt to make sense of the large tree of nonsense
it couldn’t reduce.

C++ also suffers from years of backwards-compatibility twister creating
abundant cases of ambiguity if you do something wrong. The compiler will do
it’s best, but if your code suddenly parses wildly differently because your
semicolon is missing, oh well. Rust is far simpler in that regard, so there
are fewer instances where your code might accidentally make sense to the
compiler if you have an error.

~~~
bluejekyll
> Rust is far simpler in that regard, so there are fewer instances where your
> code might accidentally make sense to the compiler if you have an error.

I say this from a place of love for Rust, but the first time you run into a
massive generic type tree error thats root cause is a type parameter three
levels deep not implementing Send (or some other trait) it takes your breath
away.

There are some great jokes about this in Deisel and Futures for example. It
makes Java generics look like a baby just starting to craw.

The error messages are good, you just need to go grab a coffee while you read
through the book on type theory that’s been dumped to your screen.

~~~
marcosdumay
Similar stuff happens on Haskell too. I guess that since the type system is an
unityped logic programming language, it is an even worse place than C++
templates. It is a smaller problem on practice only because the type
declarations are much shorter programs than C++ templates.

------
fmap
That's a nice way of introducing type state programming in an affine
programming language!

In general though, type state programming would be even nicer in a linear
language. For example, in the http server I could start writing a response
without ever writing a body. Rust would happily accept this code, because Rust
is always allowed to throw away resources.

An affine language controls the duplication of resources, while a linear
language controls both duplication and destruction of resources. In a linear
Rust, a data structure would have to implement Drop in order to be silently
deleted and in the http server "HttpResponseWritingHeaders" should not
implement Drop.

------
ammaskartik
Great write up, when I started coding in Rust, I sometimes used to get
frustrated, but then realised how helpful the checker is and really made me
think about structure and mechanics of my code, making me a better developer.

------
bluejekyll
I just sent this around to my entire (mainly Java) team. I think it might be
the best intro to describe the fundamental memory model that I’ve read.

The walk through this basic example, showing interface changes that enforce
different aspects of the code, are wonderful. It took me a long time to grok
some of these basic concepts, and this explains them so concisely.

I might use this as an intro to Rust for anyone who starting to learn the
language. Kudos to the author for expressing these concepts in such elegance.

------
i_feel_great
How do you get back something owned by something else?

~~~
Munksgaard
You don't, unless they pass it to you (as a return value or function
argument).

~~~
alrz
Or if the borrow doesn't outlive the source e.g. if a local goes out of scope.

------
Annatar
From that writeup, it seems like one is forced to program Rust in an object
oriented manner. Is it possible to program it in a functional manner like
Lisp, or a procedural manner like BASIC or assembler?

~~~
wwright
In my experience, it’s not much more OO than Haskell. Even in many functional
languages, you typically want _some_ OO — you’ll want some core type that you
export opaquely, and then you have functions doing logical operations on that.

I think two of the biggest differences are:

\- Because the type and memory systems make it possible to skip heap
allocation if you’re careful, it’s “idiomatic” to prefer avoiding techniques
that still require it. This means avoiding long-lived closures. Note that
closures still are used very heavily, but typically aren’t passed around
between many owners like in Lisp or JavaScript.

\- Similarly, because Rust _can_ give you incredible safety and speed if you
use it a certain way, people tend to write with interior mutability very
often. However, unlike in most languages, Rust’s type systems is a
_tremendous_ help to the programmer with mutability, and it’s much, much more
likely that you’ll see interior mutability done right. It allows you to
program in a style that feels very functional even while using many imperative
components. A lot of your code still ends up as basic pattern matching and
simple closures.

~~~
kibwen
Regarding closures, note that Rust closures don't allocate on the heap unless
you explicitly use a Box to put them on the heap. Long-lived closures aren't a
problem, because closures by default borrow the variables they close over (and
like all borrowed references, these cannot extend the overall lifetime of the
borrowed data); alternatively they may take ownership of their closed-over
variables, but in that case the closure can only be called exactly once (as
expected for owned values).

~~~
FreeFull
A closure taking ownership of the closed-over variables doesn't actually imply
that it is FnOnce (only callable once). You can perfectly well have a Fn or
FnMut closure that owns its captures, as long as you don't try to do any moves
inside the closure. If you need to do a move in an FnMut closure, you can also
wrap the captured variable in an Option, and use .take()

