
Emulating the Rust borrow checker with C++ move-only types - buovjaga
http://nibblestew.blogspot.com/2017/05/emulating-rust-borrow-checker-with-c.html
======
pcwalton
The Rust borrow checker checks _borrows_ : that is, references. This doesn't
emulate borrows, but rather emulates Rust-style moves.

An attempt at a more accurate statement would be that this emulates the use-
after-move checking that the Rust compiler does. The problem is that that it
doesn't do that either: it statically prevents copies but doesn't prevent use-
after-move.

I think the accurate way to describe this pattern would be that it disallows
copies and forces you to annotate moves with the move keyword. This is
somewhat similar to what Rust does, in that non-Copy types are moved by
default. The difference is that you don't have to write "std::move" in Rust:
the compiler just infers the right thing to do.

It's a little hard to map this onto Rust semantics to begin with, since
fundamentally all this is is not having a copy constructor, which is a concept
that doesn't exist in Rust in the first place.

~~~
masklinn
> The difference is that you don't have to write "std::move" in Rust: the
> compiler just infers the right thing to do.

And of course since it's the language's default you don't have to write a
dozen lines of boilerplate either, this is the C++ code stripped of the
interspersed text:

    
    
        class MoveOnlyInt final {
        private:
           int i;
    
        void release() { /* call to release function here */ }
    
        public:
            explicit MoveOnlyInt(int i) : i(i) {}
            ~MoveOnlyInt() { release(); }
    
            MoveOnlyInt() = delete;
            MoveOnlyInt(const MoveOnlyInt &) = delete;
            MoveOnlyInt& operator=(const MoveOnlyInt &) = delete;
    
            MoveOnlyInt(MoveOnlyInt &&other) { i = other.i; other.i = -1; }
            MoveOnlyInt& operator=(MoveOnlyInt &&other) { release(); i = other.i; other.i = -1; return *this; }
    
            operator int() const { return i; }
        };
    

This seems like a fair Rust version, with as you noted the "move source"
becoming inaccessible rather than just being in an invalid state:

    
    
        struct MoveOnlyInt {
            i: isize,
        }
        impl MoveOnlyInt {
            fn new(i: isize) -> MoveOnlyInt { MoveOnlyInt { i: i } }
        }
        // Rust doesn't have an "operator int", this seems close enough
        impl<'a> From<&'a MoveOnlyInt> for isize {
            fn from(m: &'a MoveOnlyInt) -> isize { m.i }
        }

~~~
kibwen
That's actually even way too much code for the Rust version. A wrapper type
declaration is as simple as `struct MoveOnlyInt(i32);`, and construction is
then just `let foo = MoveOnlyInt(42);`. (And of course, the wrapper type is
only necessary because integers in Rust implement `Copy`, opting out of move
semantics.)

~~~
steveklabnik
(and "operator int" would be foo.0 in this case)

------
throwaway91111
Isn't this just a UniquePtr? Ie it allows moves, but references to it are not
guaranteed to be valid. The latter is the borrow checker.

~~~
akubera
Yeah, the unique_ptr<T> enforces move semantics and can be "borrowed" via
get() (but unlike rust, further borrowing/moving rules are not enforced by
C++). This post is an explanation of how to write your own class which
enforces this for objects whether or not they are on the heap.

It's a good question to ask whether implementing a move-only class is more
beneficial than wrapping a _normal_ class in a unique_ptr. This comes down to
trusting the programmer to know when to copy/move/delete (for better or
worse).

The best policy is probably to implement ALL constructor/assignments so
there's minimal frustration when, say, trying to write a subclass, and have a
special subclass `UniqueFoo` that deletes copy methods to enforce Moveability
- minimizing surprise and maximizing flexibility.

------
0xFFC
Boy I am gonna get down voted, But I rather say this. I do consider myself C++
programmer, and I like it, but truth is truth. I think C++ designers are very
well respected scientist. But they are not programmers. This is sad truth.

Every version of C++ comes out they tend to make C++ more complicate and
introduce more ways to do something which was possible before. I was/am very
happy with overall design of modern C++. But they did a very bad strategic
mistake. Instead of making language simpler or at improving what was there,
they are introducing new syntax for everything. Let me give you instance.

Imagine we have metafunction is_void.

Before C++11, there was only one way to call it :

> is_void<t>::value

C++11 they added :

> bool(is_void<t> {})

C++14 they added :

>is_void<T>{}()

and there was plan to add this syntax to C++17:

>is_void_v<T>

Okay. I am not scientist. And I do know someone people in committee are well
respected scientist and tbh I like their job alot.

But when it comes practical stuff. This is nuts. Do they think how much makes
this reading code bases hard ? How much makes tooling hard ? How much makes
syntax unapproachable ?

I don't understand rational behind this, Why would they add new syntax for
what already possible, when they can improve core language/libraries more and
more.

~~~
petters
Deprecating old and arguably inferior syntax is part of the evolution of a
language.

C++ is not going anywhere, so improvements are welcome. Upgrading from is_void
to is_void_v can be done automatically.

~~~
0xFFC
FYI they didn't deprecate anything, they just augment new syntax.

------
petters
As others have pointed out, this does not really emulate the Rust borrow
checker. You can still have multiple references and pointers to the same
object.

------
youdontknowtho
And then someone heard the Rust mafia say "Form the MegaZord!!!"...and many
corrections were posted.

~~~
kibwen
Yeah, who'da thunk that saying technically inaccurate things on a site full of
pedantic nerds would meet resistance. :P

~~~
youdontknowtho
Man! Rough crowd! Is there a 'joke' tag that I'm missing or something?

~~~
adrianN
HN readers tend to downvote jokes.

~~~
dikaiosune
Not funny ones, in my experience

