If it's not asking too much, could you also point me to the C++ primitives that prevent dangling pointers as well?
EDIT: Also, the primitives that prevents the mutation of what a const reference refers to? (Much in the way that shared references and exclusive references can't co-exist in Rust)?
I'd hope the C++ primitives for the above all perform their job at compile time of course.
An extension to C++ that adds (pretty much) a type system on top of C++ to enable static analysis and prevention of race conditions. Widely used at Google.
Super interesting! That's exactly what Rust is doing too, except I think Rust's version (which pretty much relies on two "traits", `Send` and `Sync`) is much simpler, much more ergonomic, and much more complete.If so, thanks!
Through I don't yet "see it" that C++ has safety primitives that "equal" that of Rust's, I've learnt a lot about C++'s safety features from other folks' replies in this thread. So again, thanks!
You can prevent dangling pointers using strong/weak pointers or many other ways depending on the context, most examples are quite contrived though and the only "dangling pointers" I see are usually those relating to resources/handles to things from the OS where the lifetimes need to be managed more carefully, i.e. audio/video/gpu/etc, how would a rust program handle a handle to the currently playing audio device being unplugged and invalidating your handle? Every OS also has its own quirks, so how would it work cross platform?
Constexpr primatives/functions have compile time guarantees and can't be changed.
> You can prevent dangling pointers using strong/weak pointers
I presume you're talking about unique_ptr/shared_ptr/weak_ptr right? If one avoids raw pointers and uses those, I think you cannot have a shared reference without ref counting right? (because shared_ptr uses ref-counting). Not that this invalidates your point, just thought it was useful to point out.
> how would a rust program handle a handle to the currently playing audio device being unplugged and invalidating your handle?
I've never had to deal with that situation, but I presume some mechanism similar to how the mutex in the stdlib has a .lock() method that returns an Option<MutexGuard<_>> (which forces the user to acknowledge/handle the case of a poisoned mutex).
> Constexpr primatives/functions have compile time guarantees and can't be changed.
Consider the c++ code here:
int a = 4;
const int &b = a;
a += 1;
Such a thing would be disallowed in Rust, which (though hard to see from this example) is the exact mechanism that prevents a Vec being modified while it is being iterated on. Does C++ have primitives that provide such guarantees?I feel Rust is a better language than C++ at implementing correct multithreaded code, but is so constraining that it makes correct single-threaded code hard to implement when it doesn't fit neatly into tree-shaped ownership (eg. graphs, GUI trees, and extending existing program architectures).
constexpr int a = 4;
auto& b = a;
a+= 1; // compile error
b+= 1; // compile error
Trying to make b constexpr with "a" as an int is also a compile error.E.g., if you're implementing an iterator that takes a reference to the length of the container being iterated, and you require that that reference is constexpr, that means the container will have a fixed, compile-time-known, size.
Since you didn't reply to the other parts of the comment, I'll presume that C++ doesn't have the mutex I'm talking about (apart from this attempt[0], which I just learnt about and commented on in a different reply), and that it's not possible to have non-ref-counted shared references while statically avoiding dangling pointers.
void iter(std::vector<int>& vec) {
auto& vec_size = vec.size();
vec_size += 10; //compile error
}There are many ways of forcing compile errors for mutexes that people have built, but they all come with their own trade-offs.
I've not encountered a problem where I specifically needed "non-ref-counted shared references while statically avoiding dangling pointers", so I'm not an expert on it.
Thanks for the replies about the other stuff!
You would either represent this as all the operations having a Result which can be Unplugged or whatever, or you might decide (as the designer of that library) that you'll eat the error and silently ignore operations when unplugged, offering an unplugged() predicate so that callers can check whether they got unplugged if they care.
If you mean, what if the system is allowed to just invalidate handles we've got for some reason, without telling us about that, and then after invalidation they just mustn't be used, then you're screwed, regardless of programming language, that's a pretty bad design and there's nothing to be done about it.
A more common design (e.g. for OS file handles) has an explicit call where you give back the handle (close in the case of file handles), promising you won't use it again. If the handle is meanwhile broken for some reason anyway (e.g. user pulled out the USB stick with the file on it) then the OS will tell you about the problem, but you still need to acknowledge that by closing the broken handle, you don't just suddenly find there's a different file behind your existing handle now. It's no trouble to represent this properly in Rust.
Let's explain a bit more about why Rust cares about thread safety. Rust types can have marker traits named Send and Sync. Send means "You can safely give this type to another thread" and Sync means "You can safely give references to this type to another thread".
For example lets say I have a Goose, which is three 32-bit signed integers named x, y, z, plus three booleans flapping, honking and hissing. All six of those elements are Send, so Goose is Send. I can give a Goose to another thread no problem.
But the type Rc<Goose> isn't Send. Rc is a reference counted smart pointer, like shared_ptr<Goose> from C++ except it's not for threaded software. It's a little bit faster, especially on some CPUs, but you can't safely use it across threads. Rust has another reference counted smart point Arc, and Arc<Goose> is Send for the same reason shared_ptr<Goose> is thread safe in C++ -- it uses atomic integers for reference counting.
Because Rust tracks the marker traits for me, I don't need to carefully read documentation for a new type I'm using FunkyTractor to check whether it's thread safe. If it's Send then I can give it to another thread, and if it isn't then that program doesn't compile and I go "Aww" and use say a Mutex to wrap my FunkyTractor so that multiple threads can access it safely, then it will compile.
Only the people actually innovating tricky thread safety stuff (e.g. building your own spinlock) need to care about this, and make decisions like "Should this type I'm creating say it is Send?" for everybody else it's automatic.
> Comments should get more thoughtful and substantive, not less, as a topic gets more divisive.
> Please don't post shallow dismissals
If you have a real rebuttal, add it. Otherwise don't bother commenting.
To quote you: Trolling is unwelcome here.