Live data from Hacker News

Why Rust's ownership/borrowing is hard

softwaremaniacs.org

41–50 of 67 posts

Re: Why Rust's ownership/borrowing is hard

#41
post #35
post #14

"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…

> 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 appro…

Stipulated about immutability.

"I find myself rarely having to think about ownership. That could be an artifact of the kind of Python programs..."

Python still can have action-at-a-distance, where things unexpectedly change as a result of executing code. The single-threaded analog to a race condition is less severe because it's at least deterministic, but can still make programming difficult to understand.

Re: Why Rust's ownership/borrowing is hard

#42
post #35
post #14

"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…

> 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 appro…

Your operating system, and the outside world, is a shared mutable resource -- particularly file systems and network connections. Even in Haskell, you can explicitly close a file (the docs recommend it!), and if you shared that file with anyone else they'll start erroring out.

That said, programming languages (on their own) can't really do anything about other processes/computers interacting poorly with yours! The ultimate semantic of ownership is still there, though.

There's also the ST Monad, which provides safe mutability in Haskell by enforcing that the mutated state doesn't escape a region of the program. This is literally the same idea as the borrow checker.

Re: Why Rust's ownership/borrowing is hard

#43

Earlier quoted context omitted.

I thought that was one of the most fascinating parts - Rust's borrow-checker enforces the Law of Demeter and Principle of Least Privilege as a side-effect. Code that takes a full structure when it only needs to operate on a part of the structure is badly designed. It's not conveying the full information about the data that it actually needs, which means that unexpected dependencies can crop up, implicit in the body o…

>Code that takes a full structure when it only needs to operate on a part of the structure is badly designed. No, it is not; this is done all the time with methods and it improves encapsulation - you may not want your clients to be able to decompose your data structures. Do you really mark every member of your data structures as pub?? Sorry but this is a poor ad-hoc defense of an actual annoyance in the borrow-checke…

>Code that takes a full structure when it only needs to operate on a part of the structure is badly designed.

I think this was a sensible statement, especially in context; I strongly agree that "this is behind a lot of long-term maintenance messes". And lost performance.

For encapsulation in Rust, traits are used to abstract and separate concerns, but they don't force you to bundle your data into large structures.

And encapsulation isn't an end in itself. Privacy has its uses (maintaining invariants, minimizing the exposed surface area of a library, etc.) but I find often in OO codebases that encapsulation creates its own problems. There is no substitute for careful data-oriented design; no amount of `private` will prevent your teammates from working around or ripping apart your carefully shrink-wrapped objects.

There is certainly some awkwardness in the borrow checker, but also great value.

Re: Why Rust's ownership/borrowing is hard

#44
post #25
post #14

"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…

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 kee…

Worse, when in C you finally convince yourself that a particular piece of code is free of ownership issues, another programmer can come in, add a single line of code or do a "helpful" refactoring and suddenly the issues are back.

Re: Why Rust's ownership/borrowing is hard

#45
post #18

Earlier quoted context omitted.

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 no…

> 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. Most of a browser's memory consumption usually consists of JS and DOM objects. In effect, the pages you're visiting are actually doing the bulk of the allocations, not your browser.

Though each DOM object might be smaller for us. Maybe.

Gecko has troubles with leaking the DOM because it CCs the DOM. We don't need to worry about that since Spidermonkey's GC manages the Servo DOM, too.

Re: Why Rust's ownership/borrowing is hard

#46
post #32

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.

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

Re: Why Rust's ownership/borrowing is hard

#47
post #30

Earlier quoted context omitted.

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…

I guess I like Rust (certainly more than I like C++), but I agree with you, and disagree with both sister comments, in that I think type systems do introduce artifacts which are then defended as "the right thing actually if you think about it the way the type system does" but they still are artifacts. So today this helper function borrows one member and tomorrow it might need to borrow two, and every time you're supp…

OTOH sometimes you ought to think about it the way the type system does, because its own limitations reflect those of other users of your code. The biggest one is consumers of your (public) interface, whose code shouldn't fail to compile if you merely change the implementation to use an additional struct member.

Re: Why Rust's ownership/borrowing is hard

#48
post #14

"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…

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.

Re: Why Rust's ownership/borrowing is hard

#49

Earlier quoted context omitted.

I thought that was one of the most fascinating parts - Rust's borrow-checker enforces the Law of Demeter and Principle of Least Privilege as a side-effect. Code that takes a full structure when it only needs to operate on a part of the structure is badly designed. It's not conveying the full information about the data that it actually needs, which means that unexpected dependencies can crop up, implicit in the body o…

>Code that takes a full structure when it only needs to operate on a part of the structure is badly designed. No, it is not; this is done all the time with methods and it improves encapsulation - you may not want your clients to be able to decompose your data structures. Do you really mark every member of your data structures as pub?? Sorry but this is a poor ad-hoc defense of an actual annoyance in the borrow-checke…

If you're trying to achieve proper encapsulation, you just have a module that implements some sort of functionality, and shouldn't need to borrow anything from it. The real question is why you're pulling data instead of pushing messages.

Re: Why Rust's ownership/borrowing is hard

#50

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

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.

Post reply on HN