Hacker News new | ask | show | jobs
by ordu 1416 days ago
> How would you define object identity

    fn main() {
         let vs = [(), (), (), (), ()];
         for v in vs {
             println!("{:?}", v);
         }
    }
This loop will five times print "()", because `v` will iterate through all five elements of `vs`. Are this values are identical or they aren't? I don't know, it doesn't matter, isn't it? But I think of them as of different values: they are different members of `vs`.
1 comments

They are the same value, but with different identities. vs[0], vs[1] etc. are different objects, but they are all initialized with the same value. The difference is kind of irrelevant for a constant object like an empty tuple.

But imagine the following program:

  fn main() {
    let mut vs = [(1,), (1,), (1,), (1,), (1,)];
    vs[0].0=2;
    for v in vs {
      println!("{:?}", v);
    }
  }
Here we can see that identity is in fact important: `vs[0].0 = 2` only modifies one of the objects, even if all of them initially had the same value.

By the way, note that your example should be completely equivalent to the following C++ program:

  int main() {
     using namespace std;
     auto vs = vector<tuple<>>{tuple<>(), tuple<>(), tuple<>(), tuple<>()};
     for (auto v : vs) {
       cout << v; //imagine C++ actually had an implemntation of operator<< for tuples...
     }
     return 0;
  }
> They are the same value, but with different identities. vs[0], vs[1] etc. are different objects

What allows you to say that they have different identities? They are zero-sized types. Literally zero. `()` is a type and `()` its the only possible value. log(1) = 0 bit. If you look into machine code you will not find anything that you can call an object. The very existence of `()` is a shared dream of a programmer and compiler, and `()` ceased to exist after a compilation.

> But imagine the following program

In this program you are using types of size > 0bit, which allows more than 1 value of that type. But even then I wouldn't bet that they are different objects only because you changed one. If you didn't, it would be completely logical to replace them all with just one value in a memory, while pretending that there are many copies of it.

In this particular case I don't believe rustc would manage to do it even if we drop mutation from the example. And I can't think of a case when it will manage. But I wouldn't bet that such case doesn't exist.

> By the way, note that your example should be completely equivalent to the following C++ program

Hmm... And if we write in C++ something like:

    fn main() {
        let vs = [(), (), (), (), ()];
        for i in vs.iter() {
            println!("{:?} {:?}", v, v as *const ());
        }
    }
Will we get an output like this:

    () 0x7ffdae96bec8
    () 0x7ffdae96bec8
    () 0x7ffdae96bec8
    () 0x7ffdae96bec8
    () 0x7ffdae96bec8
Will all addresses be equal? If they are, then the proposition "C++ also pays a price for insisting not only that objects have addresses, but those addresses are distinct"* is false.
> What allows you to say that they have different identities?

Syntactic analogy with non-zero sized types. If you want a special case that says "there is a single object of the zero-sized type" that's ok, but it's a special case. All other types have a difference between object identity and value equality.

> Will all addresses be equal?

No, because C++ doesn't optimize for ZSTs, and it doesn't modify semantics for const. I agree that C++ pays a price for these two things, but I don't think it's because it "insists all objects have different addresses", I believe that is just a consequence to not giving special semantics to const beyond disallowing writes.