7 ms·
> In Rust, that question is answered by the borrow-checker at compile time: (About zero sized objects being the same) But why does that mean the programmer ne
by ketzu 11d ago
> In Rust, that question is answered by the borrow-checker at compile time:
(About zero sized objects being the same)
But why does that mean the programmer never has to check? (or if they want that information from the borrow checker how would they get it?) It's not motivated as the intro above for c++ was just "In C++, we might do this to check if two pointers refer to the same object".
So the borrow checker knows already, why does that stop the programmer from wanting to know or separate these cases?
- loeg 11d agoIt's not obvious to me that the borrow checker actually knows, so much as it can prove there are no illegal conflicts between mut and non-mut references to the same object. If you somehow need this property in your programs, I think you can just add a 1-byte member and use pointer equality. (I'm not a Rust expert.)
- wahern 11d agoI assumed it's a figure of speech, but, yeah, the borrow checker only enforces function-local invariants, with no interprocedural analysis or memorization. Parameter aliasing is just a logical deduction from assuming local invariants are maintained at all call sites. That's why the borrow checker imposes very minimal cost (esp when it was block based), and doesn't even require static compilation. Traits and other aspects of the type system are where the complexity and slow compile times come from. I guess maybe the semantics of ZSTs complicate the aliasing story a tad?
- TazeTSchnitzel 11d agoI'm not sure if this quite answers your question, but the difference in philosophy here is that C++ objects with pointers always have identity, whereas a Rust object only has identity if it has a non-zero size. It's an application of the zero-overhead principle, "you only pay for what you use".
- ketzu 11d ago"zero sized objects don't have identity" actually would answer my question and is a really interesting factoid, thanks!
- deleted 11d ago[deleted]
- tialaramex 11d agoThis is why C++ doesn't have ZSTs, it wants all objects to have identities, the obvious way to distinguish them is by where they are in memory, but ZSTs don't have distinct addresses in memory.
- ameliaquining 11d agoI'm not a C++ expert. Why does C++ want all objects to have identities? Presumably it has some feature or something Rust doesn't have that requires this?
- mpyne 11d agoIt's pretty deeply tied into C++'s object lifetime mechanics, which rely on storage being available and reserved for the use of that object's lifetime. If multiple objects with valid lifetimes had a situation where one lifetime needs to end, what should happen to the other objects' lifetimes? C++ actually did end up evolving the ability to define a zero-sized class without a unique memory address, but mostly to allow optimizations like the empty base optimization to apply in other situations where it could make sense, especially with templated or constexpr code.
- tialaramex 11d ago> C++ actually did end up evolving the ability to define a zero-sized class without a unique memory address Did it? Are you talking about the no_unique_address attribute (I had to go look that up because WG21 apparently doesn't care about consistently using or not using separators in attribute names) ? That attribute lets you do the same trick as empty base class but without the ceremony, however it doesn't let you make ZSTs.
- steveklabnik 11d ago> if they want that information from the borrow checker how would they get it? If you want to compare if two pointers point to the same place, you use https://doc.rust-lang.org/stable/std/ptr/fn.eq.html https://doc.rust-lang.org/stable/std/ptr/fn.eq.html Rust just defaults to value equality over reference equality. This is true for everything, not just ZSTs. (I find the post's framing of "it's stored in the borrow checker" to be a bit odd, but I can't put my finger on exactly what it is. The borrow checker doesn't determine these sorts of semantics, it checks for liveliness and aliasing, so "do these pointers alias" isn't inherently not the borrow checker's job, it just strikes me as an odd way to put it. Maybe it's because you don't "ask the borrow checker for that information" really.)
- deleted 11d ago[deleted]
- ketzu 11d agoAs the article itself discusses, pointers to zero sized objects are not necessarily different (they write it is only the case in debug mode). > I find the post's framing of "it's stored in the borrow checker" to be a bit odd That's exactly what I wanted to say as well. I feel like it would have been better to just skip the borrow checker mention and just go "in rust this can not be done reliably ..(section about pointers being the same)"
- AnyTimeTraveler 11d agoThis can be done reliably, though. You can just do if a == b { ... Just that the check will be replaced at compiletime with a constant, since the borrow-checker tracks all objects lifetimes and can use that information to optimize the check away. The objects themselves don't make it into the compiled binary, since they have no size, but all the required information about them will make it in. So you can treat them like ordinary objects and do all the usual operations on it, without wasting any memory during runtime.
- tialaramex 11d ago
- wrs 11d agoI've tried (admittedly for just a few minutes) to come up with a case where you'd need to compare two pointers to ZSTs in any real algorithm, and failed. Can you think of one?
- phire 11d agoI suspect it's actually possible to prove such an algorithm can't exist. By definition, a ZST hold no runtime data. It does hold some compile-time data based on its existence, but after compiling, that has been type erased away. Since a ZST holds zero bits of state, there can only be one valid instance of it. You can't have multiple different versions of the same ZST representing different things. So if you have one, you automatically know it's going to be equal to all other instances of the same ZST type. And not equal to any other ZSTs. There is no point doing a pointer comparison, as that gives you no extra information, type ownership is enough.
- DonHopkins 11d agoDonald Rumsfeld forgot the zero-sized unknown: you know its type, you know its complete value, but you don't know which instance it is. https://en.wikipedia.org/wiki/There_are_unknown_unknowns https://en.wikipedia.org/wiki/There_are_unknown_unknowns COM would ask the object for its canonical IUnknown pointer. Rust answers that the question has been optimized away. A ZST has one possible value, not one possible occurrence. That doesn't prove that no identity-dependent algorithm can exist; it proves that a ZST cannot carry the identity such an algorithm requires. If identity matters, it must be represented somewhere -- and then it is no longer zero-overhead. COM is essentially a language-neutral ABI built from C++-style vtables, and QueryInterface is essentially a type-safe dynamic_cast. In Rumsfeldian terms, IUnknown provides a known way to interrogate an otherwise unknown object. QueryInterface resolves known unknowns by asking about specific interface IDs; it cannot ask about unknown unknowns. https://news.ycombinator.com/item?id=12975257 https://news.ycombinator.com/item?id=12975257 > COM is essentially a formal way of using C++ vtables [1] from C and other languages, so you can create and consume components in any language, and call back and forth between them. It's a way of expressing a rational subset of how C++ classes work and format in memory, in a way that can be implemented in other languages. > It was the outcome of the C / C++ / Visual Basic language wars at Microsoft.