23 ms·
Rusty.hpp: A Borrow Checker and Memory Ownership System for C++20
- macgyverismo 2y agoThis borrow checker runs at runtime, which I find not as interesting. Everything starts to look a lot like std::unique_ptr which I think is mostly unneeded as it ads pointer indirection. Could someone explain to me when one would use this? Is it for educational purposes perhaps?
- Ygg2 2y ago> Could someone explain to me when one would use this? For memes, obviously. Me: I want Rust! Tech lead: We have Rust at home! Rust at home: rusty.hpp
- ramon156 2y agoI think it's more an "can i do this" project, rather than a product that can be used in prod
- CaptainOfCoit 2y ago> Could someone explain to me when one would use this? Is it for educational purposes perhaps? The goal/why is, as almost always, explained in the README: > rusty.hpp as the time or writing this is a very experimental thing. Its primary purpose is to experiment and test out different coding styles and exploring a different than usual C++ workspace. TLDR: it's a experiment
- zozbot234 2y agoRust does "borrow checking at runtime" with RefCell<>.
- 38 2y agoright, but RefCell is optional. if you dont use that, you get checking at compile time.
- jmax01 2y agoHey, I am the author of this, I made this mostly for the purpose of experimenting and playing around and trying out things rather than actually using this for production projects. Making a proper compile time checker is pretty complicated(possibly impossible) without actually getting into the compiler, this just intends emulate that behavior to some extent and have a similar interface. "educational purposes" -> well kinda, I had some free time and had an interesting idea perhaps
- 38 2y ago> pretty complicated(possibly impossible) Rust does it at compile time, so why cant C++? to me this detail completely kills the usefulness of this project
- steveklabnik 2y agoC++ cannot because it does not have the necessary information present in its syntax. It’s really that simple. C++ could add such syntax, but outside of what Circle is doing, I’m not aware of any real proposal to add it. Also, Google (more specifically, the Chrome folks) tried to make it work via templates, but found that it was not possible. There’s a limit to template magic, even.
- arc619 2y agoAlthough it's not as extensive as Rust's lifetime management, Nim manages to infer lifetimes without specific syntax, so is it really a syntax issue? As you say, though, C++ template magic definitely has its limits.
- steveklabnik 2y agoNim has a garbage collector. That said, you're right on some level that it's truly semantics that matter, not syntax, but you need syntax to control the semantics.
- 2y ago
- jandrewrogers 2y agoI don't think it is intended to be used in a real system, this was more of an experiment to see what was possible. C++ as a language isn't well-suited to supporting a compile-time borrow checker. The difficulty of retrofitting C++20 modules to the language is probably just a glimmer of the pain that would be involved in making a borrow checker work. There is a place for runtime borrow checking. Some safe cases in well-designed code are intrinsically un-checkable at compile-time. C++ is pretty amenable to addressing these cases using the type system to dynamically guarantee that references through a unique_ptr-like object are safe at the point of dereference. Much of what the borrow checker does at compile-time could potentially be done at runtime with the caveat that it has an overhead. This has more than a passing resemblance to how deadlock-free locking systems work. They don't actually prevent the possibility of deadlocks, as that may not be feasible, but they can detect deadlock conditions and automatically edit/repair the execution graph to eliminate the deadlock instance. If a deadlock occurs in a database and no one notices, did it really happen?
- cogman10 2y ago> Everything starts to look a lot like std::unique_ptr which I think is mostly unneeded as it ads pointer indirection. Interesting, why is this? I would have assumed the compiler could have optimized away that indirection. [1] https://godbolt.org/z/9Pqqqz5a7 https://godbolt.org/z/9Pqqqz5a7
- steveklabnik 2y agohttps://stackoverflow.com/questions/58339165/why-can-a-t-be-passed-in-register-but-a-unique-ptrt-cannot https://stackoverflow.com/questions/58339165/why-can-a-t-be-...
- tialaramex 2y agoThe C++ type system is completely inadequate for these tasks. I thought of a rather nice way to picture it, the C++ type system is like you have Roman Numerals, and so now the notation itself fights trying to understand important concepts about numbers (types). Languages with a better type system are like having Arabic Numerals, it's not a panacea, but the notation allows significant improvements in expressiveness and teachability. This analogy seems especially apt because Roman Numerals lacked zero as I understand it, and the C++ type system doesn't cope well with the idea of ZSTs nor with the Empty types which are analogous to zero in type arithmetic.
- pjmlp 2y agoActually it is more like having both Roman and Arabic Numerals on the same source code, depending on the age of the project, and the C and C++ education background of the team.
- tialaramex 2y agoI don't see any way to express something like Option<Infallible> in C++ Regardless of "age of the project" or other considerations, this doesn't seem like a particularly tricky edge case of generic programming and yet C++ is stumped AFAICT
- acka 2y agoAccording[0] to Perplexity.ai, you could use std::optional<std::monostate> to get a C++ approximation of your Rust type. I am neither an expert in modern C++ nor in Rust, but I have witnessed enough of C++'s evolution over time to know that if C++ language devs find a feature desirable enough they will do whatever it takes to frobnicate the language in order to claim support for that feature. [0] https://www.perplexity.ai/search/is-it-possible-Sd3TML68TfKveHzIkoGTNA https://www.perplexity.ai/search/is-it-possible-Sd3TML68TfKv...
- pjmlp 2y agoWhile I watch with some desmay, one of favourite languages turning beyond PL/I levels of complexity, it isn't alone in this direction. One of the reasons I am not able to follow up on C++ as much as I did in the past, isn't directly related to its complexity, rather that my main worktools, the JVM, CLR and Web ecosystems, are reaching similar levels of complexity, specially with the 6 months release candence, and there is only so much one can keep up with.
- cherryteastain 2y agoWhat's the point of adding Option<T>, Result<T,E> and Rc/Arc when std::optional, std::expected and std::shared_ptr exist?
- jeroenhd 2y agoThe Option type seems to have various standard Rust methods like expect() implemented that I don't believe std::optional has. I haven't checked recent C++ standards, but I don't believe you can use partial classes/extensions in C++ like some other OO languages to add these methods to a native type. Many helper functions commonly used in Rust also only seem to exist in C++23, which not ever project can be compiled under yet. In normal C++ code, the native types would probably be better to use, but if you're going full Rust style code, you may as well use these new types.
- blegr 2y ago> The Option type seems to have various standard Rust methods like expect() Isn't that value()?
- tialaramex 2y agopub fn expect(self, msg: &str) -> T So that says it's a method (its first parameter is the type itself, but named self rather than as a normal parameter so we can use method syntax instead of calling the function Option::expect) but it also takes an immutable reference to a string slice. That second parameter, msg, is the text for a diagnostic if/ when you're wrong. So, in a sense it's like value() but the diagnostic text means, when I was wrong... let goose = found.expect("Our goose finder should always find a goose"); ... I get a diagnostic saying that the problem is with "Our goose finder should always find a goose". Huh. I think we know where to start trouble shooting.
- vips7L 2y agoAre there no stack traces? Wouldn’t that point you to where to start trouble shooting?
- andrewstuart 2y agoAll the pain of rust PLUS all the pain of C++.
- lsaljdljsljljsa 2y agoCool idea.... I would say the secret sauce in rust is Match + Enumerations and serde... :)
- klabb3 2y agoAgreed. Maybe add immutability with copy semantics by default. And no null (through enumerations but worth pointing out). Most of the Rust debates, praise and criticism are about higher level features, but just these sane pleasurable fundamentals is the main thing I miss in most languages (mostly Go and JS in my case).
- jmax01 2y agoI get it, the Rust enum system is such a connivence, but well the secret sauce in the readme is what the "official people" say....
- senkora 2y agoSee also Circle: https://www.circle-lang.org/ https://www.circle-lang.org/ I don’t think it’s available yet, but last I heard that dev is working on a borrow checker for C++ as well.
- habibur 2y agoFor other that are wondering how C++ programmers memory managed till now -- check RAII.
- HarHarVeryFunny 2y agoCan someone familiar with both please explain the benefit of Rust's borrow checker memory management model over C++'s std::unique_ptr and shared_ptr ? Is there some safety argument to prefer Rust's model, or is it something else ? I'm not aware of any C++ compiler doing it, but it seems smart pointer overhead could be automatically and safely reduced (in same way one can do it manually) by the compiler lowering the generated code to use raw pointers where permissible.
- lifthrasiir 2y ago> it seems smart pointer overhead could be automatically and safely reduced (in same way one can do it manually) by the compiler lowering the generated code to use raw pointers where permissible. The sheer difficulty of doing this is one of the motivations behind Rust's borrow checker, which uses a combination of type system and static analyses to prove the safety without running anything. In fact this problem is probably easier to solve for languages where everything is GC-managed; those languages would have a heavy runtime which can transparently handle that in principle!
- School-Cotton 2y agoRust has unique and shared pointers too (Box and Arc/Rc). But using them when unnecessary results in extra heap allocations. I’m not aware of C++ compiler that can consistently rewrite uses of unique_ptr to heap-allocated objects to use raw pointers to stack-allocated objects instead.
- lionkor 2y agoTheres nothing special about unique_ptr, if you dont want allocations and youre ok with just moving your values around directly, you use value and move semantics.
- School-Cotton 2y agoMove and value (deep copy) semantics exist in Rust too, but neither of those does the same thing as passing a raw pointer (or reference). Which you can do in c++, but not safely. That’s the difference with Rust. In C or C++ if a function/method takes a raw pointer (or some other lifetime-constrained type like string_view), I have no idea if it’s going to stash it somewhere and try to look at it again later. If it returns a raw pointer or reference, I don’t know whether it is going to get invalidated by some future call. Iterator invalidation is a huge source of UB in C++ but completely unknown in rust. Clearly having a hash map where all the values are stored indirectly in shared_ptr would let you provide a safe access API, but would be horrible for performance. In Rust you can have the safe API without compromising on efficiency.
- monax 2y agoI built a whole operating system using ideas transplanted from Rust into C++ https://github.com/skift-org/skift https://github.com/skift-org/skift
- vlovich123 2y agoOne huge caution about this - this uses RefCell*-like semantics which means that the borrow/borrow_mut checking is not thread-safe. This is dangerous because in the docs they have examples of shared_ptr in there but using that from multiple threads would be UB - there's 0 cases where this + shared_ptr makes sense unless you transparently upgraded to an atomics-based variant. Similarly, in a thread-aware implementation you'd expect more efficient handling of locks as well (i.e. borrow / borrow_mut would just acquire a lock and return a proxy without any additional borrow checks). The other footgun is that there's no concept of a non-owning pointer which is dangerous - there are several equally dominant conventions in C++: naked pointers might be heap allocated, it might represent an optional const&, or it might be a pointer to the stack. Ingesting naked pointers should probably require an explicit annotation instead of assuming it's a new'ed pointer. It's a neat idea, but I suspect this particular implementation is likely to introduce more UB, not less, because of the thread-safety footguns. In a single-threaded system, the borrow checker doesn't add a huge amount. The biggest gain is of lifetime enforcement which this doesn't get you. Also because you have to construct these Vals at point of initialization of your value, it's viral. Upgrading input arguments to use this can be dangerous if dealing with pointers. * For C++ users, RefCell is a compile-time borrow checker escape hatch to do the checking at runtime instead - you can borrow immutably as many times xor borrow once mutably - anything else is a abort.
- jacobgorm 2y agoAnything to not have to use cargo and crates.io.
- projektfu 2y agoauto foo2 = foo0; // foo0's ownership is not transfered to foo2 was this supposed to say "now transferred"?
- jmax01 2y agoYes thats a typo