Is any way to know references are referencing the same object?


#1

I’m convinced, questions like this were asked many times, but searching on this forum is not successfull for me. If this is fundametal limitation of Rust, why?


#2

You can cast the references to a *const _ to get the ptr addr, and then compare. This won’t work if you have more indirection (e.g. one reference is a &&&&&… and the other is different) though, but should for basic cases (where ptr equality is sufficient, of course). Maybe there’s another way that someone knows.


#3

Hm. My gut instinct would be to exercise caution with pointers; I’m not certain if rust provides any explicit guarantee that pointers preserve identity, or if the compiler is free to optimize these details away; there was some discussion at some point that Rust needs a memory model.

Speaking of which, there’s a test case on the “rust memory model” repository which demonstrates a pointer equality comparison that has different results on debug and release.


#4

Indeed, I’m not sure if casting a ref to a pointer and then comparing them is guaranteed to work to determine if they point at the same value. Presumably if you have two references then they should at least be valid (assuming no funny business to produce them occurred) pointers.


#5

I’m not sure that i really need pointers comparison. I’m just learning Rust, and may be i’m on the wrong way. May be, it is not “Rust-way” to compair equality of references.

This is a situation, where i’v got my question - following the chapter from “The Book” https://doc.rust-lang.org/stable/book/associated-types.html, i’m trying to make less trivial implementation of Graph trait from this chapter:

trait Graph {
    type N;
    type E;

    fn has_edge(&self, &Self::N, &Self::N) -> bool;
    fn edges(&self, &Self::N) -> Vec<Self::E>;
}

As example, a have chosen for implementation a vector of pairs of references to Node structs

type MyGraph<'a> = Vec<(&'a Node, &'a Node)>;

impl Graph for MyGraph {
    type N = Node;
...

Of course, Nodes have to be stored/owned elsewhere with longer lifetime, but it is not the point of my question.

In order to implement such Graph trait, i have to test that parameters of methods and one of elements of pairs from vector are referencing the same Node object. Even i’ll choose another implementation of Graph trait, the problem will remain the same due to specification of Graph trait (parameter by reference). What would “The Book” imply, If Graph trait was implemented less trivial?


#6

You can use pointer equality. I believe this is totally safe and guaranteed to be correct. As implied by vitalyd, you just cast the references to *const types and do an equality comparison:

ref1 as *const _ == ref2 as *const _

Since you never dereference the *const pointers, this is safe code.


#7

I would probably separate node “ids” from nodes entirely:

trait Graph {
    // (these should simple, usually `Copy` types; they're just ids)
    type Node;
    type Edge;
    // (these can contain whatever)
    type NodeData;
    type EdgeData;

    // required methods...
    fn node_data(&self, node: Self::Node) -> &Self::NodeData;
    fn edge_data(&self, edge: Self::Edge) -> &Self::EdgeData;

    fn nodes(&self) -> Vec<Self::Node>;
    fn edges(&self) -> Vec<Self::Edge>;

    fn node_edge(&self, s: Self::Node, t: Self::Node) -> Option<&Self::Edge>;

    // helpers for ergonomic purposes...
    fn has_edge(&self, s: Self::Node, t: Self::Node) -> bool {
        self.node_edge(s, t).is_some()
    }

    fn node_edge_data(&self, s: Self::Node, t: Self::Node) -> Option<Self::EdgeData> {
        self.node_edge(s, t).map(|e| self.edge_data(e))
    }

    // etc. ...
}

Edit: Oh, hm, but for this to be even reasonably useful we’ll need these required methods too:

    fn source(&self, edge: Self::Edge) -> Self::Node;
    fn target(&self, edge: Self::Edge) -> Self::Node;
    // or maybe just `endpoints(Edge) -> [Node; 2]` for undirected
    // ... or `other_endpoint(Node, Edge) -> Node`;
    // blaargh

and perhaps the trait should also be aware of edge weights, too…

and then… aw geez, I could be here all day. I suppose that designing a trait without knowing the problem to be solved is, in a way, putting the cart before the horse.