In Rust I have a trait Node
. I would like to determine if two &dyn Node
refer to the same object.
I understand that &dyn Node
is implemented as a fat pointer, with a base address and a vtable address. The obvious approach:
fn same_node(n1: &dyn Node, n2: &dyn Node) -> bool {
std::ptr::eq(n1, n2)
}
may falsely report the same object as different, if one of the dyns was constructed via a supertrait. Rust playground example. The base pointers are the same, but the vtables are different, despite it being the same object.
But comparing ONLY the base pointers may falsely report different objects as the same. For example, a Node
may have another Node
as its first field; these will have the same base pointer but different vtables. These should be treated as different.
The best solution I've found is to add an as_node()
function to the Node trait itself; this "normalizes" the vtable and then the fat pointer comparison works:
fn same_node(n1: &dyn Node, n2: &dyn Node) -> bool {
std::ptr::eq(n1.as_node(), n2.as_node())
}
This works (playground link) but is extremely subtle and also incurs unwanted dynamic dispatch.
What's the best approach here? Thanks for any help!
Your as_node()
method isn't guaranteed to work. Rust does not provide any guarantees about comparing vtables whatsoever.
The only way I can think about that can work is adding Any
as a supertrait, and comparing the TypeId
and the thin pointer. A Node
cannot contain a Node
of the same type as a field - otherwise it would have an infinite size.
This can still break down for zero-sized types, though, and will require 'static
(although there are tricks to avoid that).