How would you define object identity if different objects can have the same value and the same address?
To my mind, an object is a value with a unique identity. How else would you define it?
And if you want a billion empty values, then yes, those could all be implemented by the same object - it's pretty easy to implement, even though it would be ncie for a compiler to do it automatically (like how Java normally gives you the same Integer object if you box the same int value in two different places, even though it will give you a different Integer of the same value if you explicitly ask for it with new).
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`.
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 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.
To my mind, an object is a value with a unique identity. How else would you define it?
And if you want a billion empty values, then yes, those could all be implemented by the same object - it's pretty easy to implement, even though it would be ncie for a compiler to do it automatically (like how Java normally gives you the same Integer object if you box the same int value in two different places, even though it will give you a different Integer of the same value if you explicitly ask for it with new).