top | item 11093389

Why Rust's ownership/borrowing is hard

122 points| wkornewald | 10 years ago |softwaremaniacs.org

64 comments

order

jerf|10 years ago

"Rust's ownership/borrowing system is hard because it creates a whole new class of side effects."

I'd submit it doesn't create them... it reveals them. They've always been there. Almost every other language fails to shine the light on them, but that doesn't mean they aren't there. All GC'ed languages still have issues of ownership, especially if threaded, and all non-GC'ed languages have all the issues Rust has... it's just that the language doesn't help you.

dpc_pw|10 years ago

After coding in Rust, I got paranoid about writting any C. I always knew the issues of ownership, concurrent access, allocation etc. are there, and I had to think about them anyway, but after explicitly dealing with them in Rust all the time, I see them now so clearly. They're everywhere, and without language support it's so easy to overlook eg. iterator invalidation issues, or aliasing.

Sure, in practice, if you keep your code neat and well designed, and hopefully not too big, it's hard to really trigger such bugs, but in a bigger codebase it's so easy to overlook them...

klodolph|10 years ago

Don't be so quick to say that Rust is doing the "right thing" here. An early example in the article shows one of the big Rust gotchas: you want to borrow a reference to a part of a structure, but if you encapsulate that in a function which takes a reference to the enclosing structure, the entire enclosing structure is borrowed. This isn't a revealed side effect, this is an invented side effect that is an artifact of the type system.

Animats|10 years ago

Exactly. C programming has three big questions: "How big is it", "who releases it", and "who locks it". The language gives little help with any of those issues. C++ tries to address all three, but the mechanisms were all painfully retrofitted using templates and they leak.

In the example in the article, "is_origin(point)", the code for which is not shown, is clearly bogus. A function that's just a predicate should not consume its input. It should use read-only access by reference.

One big advantage of Rust is that, because the ownership checking is safe, you don't have to make copies of things just to simplify memory allocation control. In some C++ GUI libraries, strings are copied again and again to prevent memory allocation errors. Rust should be more efficient. It's going to be interesting to see if Servo puts a dent in browser memory consumption. It's insane that browsers now can need more than 1GB of RAM. There have to be multiple copies of the same data.

pron|10 years ago

> I'd submit it doesn't create them... it reveals them. They've always been there.

Hopefully, but not necessarily. Any (decidable) type system rejects well-typed programs (i.e., it "uncovers" problems that are not actually there), and the borrow checker is no exception. You will write some correct Rust programs that the borrow-checker just can't verify. This means that you will need to explain yourself in more detail (through more work) to the type checker, even though there was no mistake in your program. I think this is a good thing (depending on your requirements), but it isn't free.

rntz|10 years ago

> All GC'ed languages still have issues of ownership, especially if threaded,

This is not true. Ownership is only relevant in the presence of mutability. In a GC'ed language where most or all data is immutable, one rarely needs to think about ownership. In Rust, one needs to think about it all the time.

Rust is certainly a leg up compared to trying to write multithreaded C or C++ code, but that doesn't mean its approach is free of drawbacks.

Empirically, even in Python - a mutable-by-default language - I find myself rarely having to think about ownership. That could be an artifact of the kind of Python programs I find myself writing, though. I'd be interested to hear other people's experiences on that front.

hinkley|10 years ago

When people asked my why I used Java this was pretty much my answer. I got tired of having to design all of my code around figuring out who owned the arguments, and what happened when it wasn't always the same answer. I can call a method that borrows the value from one that takes ownership, but I can't do vice versa.

I have six other things that will kill my app faster than a memory leak, but I have to design this shit first? No thank you. Rust is on my list of things to learn in 2016 and I'm hoping its borrowing semantics will feel like a solution to this problem without having to sign up for nondeterministic application pauses in the bargain.

pdpi|10 years ago

Same as Haskell and IO, really. Every interesting language has side-effects, Haskell just forces you to be explicit about where you're using them.

jupp0r|10 years ago

Coming from a mainly C++ background, I find that the Rust language makes best practices in languages with manual memory management (and to some degree also in garbage collected environments) explicit. This is a great property of the language and it definitely changed the my C++ programming. You can see it as automatization of the more boring aspects of code reviews.

Manishearth|10 years ago

I am from a more mixed background, but I have had my fair share of C++ before I learned Rust.

Now when I code C++ my Rust knowledge is a double edged sword. On one hand, I have a much better idea on how to manage my data in C++. I had this discipline before learning Rust, but I didn't have explicit rules to it; it was just a ... nebulous bunch of idea about how data works. Now it's explicit. On the other hand, I am absolutely terrified when writing C++ code (something I would do with ease in the past). Well, not exactly, but it's hard to accept somewhat-unsafe code (which is probably safe at a macro level -- i.e. safe when looked at in the context of its use) and while I can see that something is safe, I can also see how it could become unsafe. And I fret about it. Rust trains you to fret about it (more than you would in C++, that is), and simultaneously Rust handles it for you so you don't have to fret about it :) C++ doesn't handle it, but you still fret about it sicne Rust taught you to.

I guess it's a "Living is easy with eyes closed" thing :P

dikaiosune|10 years ago

There were also some interesting comments yesterday when this was posted to the Rust subreddit:

https://www.reddit.com/r/rust/comments/45gcmh/why_rusts_owne...

gulpahum|10 years ago

There was one good point, which was also my conclusion:

"Given all that, I wonder if it makes sense to prefer plain old functions most of the time. Is that right, or am I overlooking something?"

The response was yes. Avoid impl methods which take a mutable self.

viperscape|10 years ago

Try a temporary variable for state, then use the consume in a scoped let block.

cm3|10 years ago

Why is move the default? In many code bases the number of immutable references outweighs that of pointers.

jonreem|10 years ago

In early rust, you actually did need to explicitly write `move` to move a value. However, this was extremely annoying as you move values a lot so moving was changed to be the default, which is far more tolerable.

EDIT: There still is a `move` keyword, but it is used to indicate that closures should take ownership of their environment vs. just borrow values from it, not to move individual values.

steveklabnik|10 years ago

That way the syntax is the same everywhere. Reference by default would look different, and then you'd need a sigil for move, etc.

pcwalton said that back in the 0.1 days this was actually implemented, and it was very confusing.

dikaiosune|10 years ago

For me at least, it's nice to have a language feature which tells me "BTW, the borrow checker is about to start caring about how long this memory is around." As opposed to the opposite, which is that everything you ever do will trigger the lifetime checks for passing arguments, and you would have to explicitly tell it to go away and that you want ownership to move.

Edit: I'm referring to the `&` operator which creates a reference/pointer to the memory it precedes.

imtringued|10 years ago

>Nothing in the experience of most programmers would prepare them to point suddenly stopping working after being passed to is_origin()!

I'm not even a Rust programmer and only read three paragraphs about the borrow checker and I instantly saw that point is moved.

jorgecurio|10 years ago

Should I learn Rust in 2016? What are you guys building with it and why Rust in particular?

dikaiosune|10 years ago

I've found it very fruitful. I had previously spent a couple of months hacking away at a project in C++ which needed lots of fine-grained parallelism and custom data structures (metagenomics analysis tool). When we decided to shift our approach and that we wouldn't be saving any code, I started out trying Rust and fell in love. Many of the issues I faced in trying to quickly put together an application in C++ were just non-issues as a result of Rust's type system.

pdpi|10 years ago

I'm building a music synthesiser in Rust, and it's a joy to work with.

If you're used to writing systems languages, you might want to learn it because it's the highest-level language I know that prays to the gods of zero cost abstraction. You still get all the control from C/C++ while gaining several convenient features. ADTs and pattern matching are personal favourites. It's widely known that Rust automates a lot of manual memory management without a GC, but it's not as widely known that the same language features also provide very very good assurances for concurrency: it forces you to acquire locks before you can touch synchronised data, it ensures that good behaviour around multiple consumers or single producers for shared memory, etc.

If you're used to writing in the likes of javascript, python or ruby, Rust is a wonderful gateway drug to systems programming, and it's probably the most accessible alternative. Instead of seeing the compiler/borrow checker as bitching and moaning at _everything_, you should see it as holding your hand and helping you navigate some of the trickier bits in writing safe code. As a bonus, it's probably one of the sanest languages you can use for writing FFI code for your language of choice when you just need the extra performance.

If you're a functional programming fan, you'll soon get the sneaking suspicion that Rust is a wolf in sheep's clothing. It's an expression based language with immutability by default, it features Algebraic Data Types and Pattern Matching, uses Result<> (think Haskell's Either) for error signalling instead of exceptions. The closest thing to "object orientation" are traits, which are actually much closer to type classes.

ajdlinux|10 years ago

Yes. I'm still just learning it myself - I've got to say it's a really nice break from writing C (which is most of my day job). Having a proper type system is really nice, and having type inference makes me feel like I'm coding in a dynamic language even though I'm not.

I'm currently building a CI tool (which will hopefully be open sourced once I get approval from our legal department) with a colleague of mine who mostly chose to use Rust because he needed an excuse to learn it...

jabl|10 years ago

I'm just learning it, so I'm not (yet, at least ) doing anything awesome with it.

As for why Rust in particular, over the years I've convinced myself that software quality is one of the big ails of, well, anything which concerns itself with the creation of SW. Which is more or less anything these days in technical professions. Strong static typing, memory safety, data race freedom etc. are of course no panacea, but are at least IMHO a step in the right direction.

FWIW, I also think Haskell is totally awesome, but with my background in mostly procedural/OO languages and with small kids at home, I feel I cannot at the moment afford the time to become really productive in a pure lazy functional language in a reasonable time frame. It's on my someday TODO list, though. Oh, and I'm somewhat of a speed freak. So hence Rust.

pjmlp|10 years ago

Yes, the more languages the better, even if you don't use them on the day job, you will be exposed to new ways of thinking and problem solving.

On my case, just dabbling given my background as language geek.