I wrote up a whole thing which, after seeing @Cerber-Ursi's reply, I think is about me misunderstanding what you were getting at, but anyway:
A whole thing
Hmm, well
x
is an i32
with value 5
y
is an &i32
pointing at x
*y
is a *&i32
is an i32
with whatever value is pointed at (5
)
So assert_eq!(5, *y)
is assert_eq!(5, 5)
which succeeds.
assert_eq!(&5, y)
would defer to assert_eq!(*&5, *y)
which would be assert_eq!(5, 5)
again.
Even more accurately, in the playground under Tools, you can get a view of the expanded macros (or on the command line with something like cargo expand.
So we can see that assert_eq!(5, *y)
expands into something like
match (&5, &*y) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
It's just going to use whatever ==
would use, which in this case is "dereference until you hit a non-reference and then compare those values".
The whole formatting mechanism is less decipherable and direct, but again a &T
or &&&&&&&&&T
is going to format the same as a T
unless you use the {:p}
flag.
Neither of the "see through reference" capabilities are magic; that's how they define the fmt
traits and the PartialEq
trait.
In particular, we can see this blanket implementation and if you click source
you'll see it just compares the derefences, and for fmt
from here or similar you can see
fmt_refs! { Debug, Display, Octal, Binary, LowerHex, UpperHex, LowerExp, UpperExp }
and then control-f for fmt_refs
to find this where again it is just deferring to the dereferenced version.
In contrast with the Pointer
implementation which calls the *const T
version for &T
which ultimately formats ptr.addr()
.
As for the *const T
implementation for PartialEq
, only here do we hit "magic" -- a compiler built-in is really what performs pointer equality (or this implementation would self-recurse). I tried to find the PR for you but it's ancient in Rust years.
See also core::ptr::eq
.
But really, if you're messing around with addresses and pointer values, there's a lot of footguns for one and you're probably in unsafe
Rust using *const T
or *mut T
for another. In typical safe Rust, if you have a &T
or a &&&&T
, you probably only really "care" about the value of the T
.
Looking at the implementation of PartialEq
for references, you can see that it is for comparing &B
to &A
when you can compare B
to A
, i.e. the same number of references have to be added to both sides.