4 ms·
The only types in the Rust standard library that do reference counting are `Rc` and `Arc` (which are both implemented in Rust). Borrow checking is done statica
by jhomedall 7y ago
The only types in the Rust standard library that do reference counting are `Rc` and `Arc` (which are both implemented in Rust).
Borrow checking is done statically.
- gmueckl 7y agoBut borrow checking doesn't cover heap allocations.
- jcranmer 7y agoYes it does. Borrow guarantees that the lifetime of the borrower does not exceed the lifetime can provide. Heap allocations have a lifetime--the start of a Box<> is the call to malloc, and the end is the call to free. Borrow checking prevents you from using the value after the free.
- geofft 7y agoHuh? It absolutely does, you can store a Box<T> somewhere and get multiple &'a Ts out of it, where 'a is limited to the lifetime of the box, but only one &'a mut T out of it at once.
- gmueckl 7y agoNo, this does not mean that the borrow checker understands heap allocations. Box is just a wrapper to shoehorn heap management into the stack based analysis that the borrow checker is able to perform. In other words, Box<> is just the equivalent of std::unique_ptr in C++.
- dthul 7y agoI see what you mean, but I think there are two orthogonal issues here. One is the ownership/lifetime of the heap allocation. That is indeed coupled to a stack object, because non-stack bound lifetimes can (with the current state-of-the-art) not be analyzed at compile time. Calling this shoehorning is strange, that's the whole idea of RAII. The other issue is borrows of heap objects, which the borrow checker can of course handle since they are no different than borrows to stack objects.
- roca 7y agoYes, it's very similar to unique_ptr. The point here is that you can implement many kinds of complex and useful heap-allocated data structures in safe Rust code, without any reference counting, and the compiler will verify that you have no use-after-free bugs, or any other kind of memory-safety bug. The same is not true of (pre-SPARK) Ada, or C, or C++, or BetterC.
- pjmlp 7y agoYou can play around with C++ lifetime analysis in Visual C++ 2019 and clang tidy. It is still WIP, yet to be deployed at large, naturally doesn't cover binary libraries, but it already a very good improvement.
- dthul 7y agoIt's exciting to see C++ pick up some of those ideas! Anything that improves the current situation is appreciated.
- pjmlp 7y agoThat is what I see as most valuable contribution from Rust. Even if Rust would fail at large scale mainstream adoption, the fact that the community has pickup up Cyclon and ATS ideas to the point that Swift, OCaml, Haskell, Ada, C++, D, Chapel, ParaSail and eventually other communities started to adopt similar ideas, that alone is a major victory to everyone involved in Rust.
- vardump 7y agoThat's interesting, still on VS2017 for Windows stuff. I can't wait to try that on kernel drivers, etc.
- pjmlp 7y agoYou can read more about here, https://herbsutter.com/2019/05/03/eurollvm-lifetime-talk-by-gabor-horvath-and-matthias-gehre/ https://herbsutter.com/2019/05/03/eurollvm-lifetime-talk-by-... https://devblogs.microsoft.com/cppblog/lifetime-profile-update-in-visual-studio-2019-preview-2/ https://devblogs.microsoft.com/cppblog/lifetime-profile-upda...
- pcwalton 7y agoBox is just a library type; the borrow checker is a compiler feature. Different things entirely. I think what you're trying to get at is that the borrow checker is based on static lifetimes (that's not the same thing as a "stack-based analysis", by the way). That's true. But that's simply because most lifetimes in practice follow simple static patterns. This is the same observation that motivates RAII in C++. Because heap management tends to follow the same few patterns over and over, judicious use of Rust compiler features and standard libraries can eliminate a lot of problems.
- geofft 7y agoA good example of the borrow-checker against data that is 100% not on the stack is crossbeam-epoch: https://docs.rs/crossbeam/0.5.0/crossbeam/epoch/struct.Guard.html https://docs.rs/crossbeam/0.5.0/crossbeam/epoch/struct.Guard... Crossbeam uses "epoch-based memory reclamation," a strategy for maintaining an object shared across threads without either locks or a global garbage collector. The tl;dr of the strategy is that there's a concept of "epochs," and if you update an object, you have to keep the old copy of the object around until the epoch is over. How long an epoch is alive is determined by how slow your readers are. So, to access some data, you create a Guard object and pass a reference to that Guard object into the functions that access Crossbeam-protected objects. You then get a reference whose lifetime is bounded by the lifetime of your Guard object. (Typically you're going to create the Guard object in your local stack frame, but nobody's stopping you from putting a Guard object on the heap, getting an arbitrarily-long reference to an object, and blocking reclamation for arbitrarily long, if you really want.) You can safely access the data through this reference until its lifetime is over, and other threads won't reclaim the data (i.e., deallocate it from the heap) until your Guard object is gone. The implementation uses unsafe, but that's not surprising, the implementation of Box itself uses unsafe so it can call malloc/free or whatever your platform equivalent is. What's important is that the safe interface can translate the requirements on paper into requirements that the borrow checker can check, using this Guard object. std::unique_ptr can't do that. (Also more generally, std::unique_ptr isn't memory-safe - see the example in https://alexgaynor.net/2019/apr/21/modern-c++-wont-save-us/ https://alexgaynor.net/2019/apr/21/modern-c++-wont-save-us/ .) There's no way in C++ to say "Here's a reference to some data in this unique_ptr, and by the way it's perfectly safe to have more than one reference to it, but you can't hold onto this reference forever because I'd like to deallocate it soon." We're already finding that the Crossbeam-style guard pattern is helping us express kernel RCU in ways that are safer than what could be done in C. Namely, there are functions that expect to be called inside an RCU read-side critical section but have no way of enforcing that in C other than by adding runtime checks for the current state of RCU. In Rust we can pass the Guard object around and ensure that readers a) create a critical section and b) don't continue to dereference data once they've declared the end of their critical section.
- dthul 7y agoSure it does. The borrow checker is concerned about lifetimes, regardless of whether the object lives on the stack or the heap.