

Join the discussion
Write your take first — we'll ask for email only when you're ready to publish.
- Hacker News
- The title includes the word “visualizing” but there is not a single diagram in this article. Is English not the first language of the author?by 7e
- Isn't a cat in a box cue enough for you?by eviks
- I never did well in English and I usually don’t know it when I see it. But it was immediately evident that the writing style of this blog was sparking joy. Even the intro section was just so well structured that I had to read it again.by Waterluvian
- > 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?
by ketzu - It'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.)
by loeg - I'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?by wrs
- I'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".
- > 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
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.)
by steveklabnik - Very nice. As a follow up would be interesting to also reverse engineer the structure of the vtable itself. I guess it’s a list of pointers to the method implementations?
- My first question was, if the vtable only has a single function in it (as in these examples), can the compiler optimize it to a function pointer to save an indirection?by derf_
- Not just pointers, it also has info such as alignment. (Shameless plug) I wrote a relevant article recently: https://ashdnazg.github.io/articles/26/Downcasting-Arcs-in-R...by ashdnazg
- This was a great read. We need more books like rust for c++, or rust for Java coders. You already have a good grasp of programming, but learning things like this is never well explained in books that try to learn you programming from scratch in their new language. Those books are too introductionary and therefore quite tedious.by Sjonny
- One problem in trying to teach a "conversion" type approach is that if you didn't really understand a thing in language A where it was maybe not too important, what do we tell you in language B where it's identical (or even mostly identical) but now crucial ?
If we teach you the important thing, language A people who did know wonder why we're wasting their time on a concept they knew, but if we do not teach it, anybody who got by in language A without this understanding is now screwed in B where it's very important.
by tialaramex - > A trait must follow so-called object safety rules to be used as a trait object
This seems for me to be a major design flaw of Rust. It tries to repurpose traits for dynamic polymorphism, even if this doesn't fit perfectly. C++ is more honest, it has two separate mechanisms for static polymorphism (templates) and dynamic polymorphism (inheritance).
by Panzerschrek - I don't see how the first rule is a major design flaw.
The second rule is mildly annoying but all it means is that dyn compatible traits must use dyn compatible traits in their function signatures so you have to duplicate it for the compile time dispatch and the runtime dispatch.
by imtringued - I'm not a C++er, but I sorely miss this is nearly every non-Haskell language.
I have a type capable of some behavior, e.g ToString. I also want to write general code operating on such objects (highlighting, obfuscating passwords, escaping, etc.).
But I have zero desire to throw away its static type information. If it gets stored in an Escaped<T>, the compiler ought to know T is still my type.
by mrkeen - In the contrary it’s one of the best design decisions of the language. The rules for object safety are the same C++ applies to virtual functions. On the other hand you don’t have to choose a priori whether you’ll need dynamic or static polymorphism, you can choose at use site, and the size of the objects do not pay for dynamic dispatch because the vtable pointer is kept together with the object pointer, not within the object.by gignico
- I once faced a tricky bug involving fat pointers (containing virtual tables) in Rust. Two such pointers may be distinct, even if they reference to the same object, because (for some reason) the compiler may create two (or even more) copies of the virtual functions table and use them in different places.by Panzerschrek
- Rust has super traits but does not support subtyping. The way super traits work is that they merge all the vtables into one big vtable. However, you can still have a direct dyn trait reference to the sub traits.
This means you need a separate vtable for all super traits and individual traits.
by imtringued - > for some reason
Just to expand on this: which vtables will be needed is not known before running codegen. At that point however multiple codegen units might generate the same vtable, and that might not get deduplicated by a later pass.
Moreover with dynamic linking (especially on Windows) it's impossible to guarantee that such vtables will be unique.
by SkiFire13 - In my own journey of discovery I found https://cheats.rs/ very helpful, and in particular its "memory layout" section has visualizations. (No affiliation with the site, just a happy reader!)by evmar
- This has a section on Object Safety, I checked and the article was written this week, but "Object Safety" is a confusing name for this idea, and so for a little while now Rust calls this idea "dyn compatibility" because the most important thing you're getting if a trait is "dyn compatible" is that you can use "dyn Trait" - https://doc.rust-lang.org/1.98.1/reference/items/traits.html...
That link more comprehensively explains the rules too.
[Edit: Realized the end of the article explains this, the author began writing it months ago, likely before they read about the improved "dyn compatibility" naming]
by tialaramex - The aside about C++ seems a little confused too?
> C++ doesn’t have this problem at all, since virtual dispatch always goes through pointers and return types are always pointers too.
Uh. C++ can return objects by value. Maybe it isn't idiomatic. And I don't know if there's a convenient spelling like `Self`. But it runs into the same problem, of course -- you would need to know the concrete value type returned to have storage for it.
And Rust has the ~same solution as imagined for C++ here, I think? Have a `DynClone` trait that returns `Box<dyn Trait>` instead of `Self`.
by loeg