Could someone explain to me when one would use this? Is it for educational purposes perhaps?
Could someone explain to me when one would use this? Is it for educational purposes perhaps?
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?
Rust does it at compile time, so why cant C++? to me this detail completely kills the usefulness of this project
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.
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.
Where Rust won't compile when a lifetime can't be determined, IIRC Nim's static analysis will make a copy (and tell you), so it's more as a performance optimisation than for correctness.
Regardless of the details and extent of the borrow checking, however, it shows that it's possible in principle to infer lifetimes without explicit annotation. So, perhaps C++ could support it.
As you say, it's the semantics of the syntax that matter. I'm not familiar with C++'s compiler internals though so it could be impractical.
Wait, how does that work? For example, take the following Rust function with insufficient lifetime specifiers:
pub fn lt(x: &i32, y: &i32) -> &i32 {
if x < y { x } else { y }
}
You're saying Nim will change one/all of those references to copies and will also emit warnings saying it did that?Writing an equivalent program is a bit weird because: 1) Nim does not distinguish between owned and borrowed types in the parameters (except wrt. lent which is bugged and only for optimizations), 2) Nim copies all structures smaller than $THRESHOLD regardless (the threshold is only slightly larger than a pointer but definitely includes all integer types - it's somewhere in the manual) and 3) similarly, not having a way to explicitly return borrows cuts out much of the complexity of lifetimes regardless, since it'll just fall back on reference counting. The TL;DR here though is no, unless I'm mistaken, Nim will fall back on reference counting here (were points 1 and 2 changed).
For clarity as to Nim's memory model: it can be thought of as ownership-optimized reference counting. It's basically the same model as Koka (a research language from Microsoft). If you want to learn more about it, because it is very neat and an exceptionally good tradeoff between performance/ease of use/determinism IMO, I would suggest reading the papers on Perseus as the Nim implementation is not very well-documented. (IIRC the main difference between Koka and Nim's implementation is that Nim frees at the end of scope while Koka frees at the point of last use.)
You're right. I was sure I read that it would announce when it does a copy over a sink but now I look for it I can't find it!
> The static analysis is not very transparent.
There is '--expandArc' which shows the compile time transformations performed but that's a bit more in depth.
Thanks for the explanation and the reading suggestions! I'll see about taking a look.
I still think that my overall point stands: sure, you can treat this as an optimization pass, but that kind of overhead isn't acceptable in the C++/Rust world. And syntax is how you communicate programmer intent, to resolve the sorts of ambiguous cases described in some other comments here.
I am again reminded of escape analysis https://steveklabnik.com/writing/borrow-checking-escape-anal...
For memes, obviously.
Me: I want Rust!
Tech lead: We have Rust at home!
Rust at home: rusty.hpp
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
Interesting, why is this? I would have assumed the compiler could have optimized away that indirection.