crystal icon indicating copy to clipboard operation
crystal copied to clipboard

Pointer equality for `Slice`

Open straight-shoota opened this issue 1 year ago • 4 comments

Slice implements the equality operator == as structural equality, i.e. two instances are considered equal if they have equivalent content. Two instances are considered equal if they have the same values: Slice[1, 2, 3] == Slice[1, 2, 3] (or even just values expressing the same concept in different types: Slice[1.0, 2.0, 3.0] == Slice[1, 2, 3]).

There is no method for referential equality, i.e. if two instances pointing to the same memory. Slice is a struct type but it wraps a pointer, so it has semantics similar to a reference type. Referential equality would be similar to equality of the pointers, but checking size as well - Slice is a pointer plus size.

Usually this is implemented as #same? and I think this would work well for Slice.

slice = Slice[1, 2, 3]
slice.same?(slice)           # => true
slice.same? Slice[1, 2, 3]   # => false
slice.same?(slice + 1)       # => false
(slice + 1).same?(slice + 1) # => true
slice.same?(slice[0, 2])     # => false

Related to referential equality and the implementation of same? is also #object_id. For example Set is a struct type but behaves like a reference because it's a wrapper of an reference type. It implements both #same? and #object_id (which just delegate to the wrapped reference). This doesn't work for Slice because the wrapped pointer is not the only defining characteristic. Size is relevant as well. So I don't think #object_id makes sense for Slice because the pointer address is not unique. Two Slice instances can share the same pointer address, but with different sizes they would still be referentially unequal.

straight-shoota avatar Jun 15 '24 15:06 straight-shoota

I think this example should be true instead of false?

slice == Slice[1, 2, 3]      # => false

ysbaddaden avatar Jun 17 '24 09:06 ysbaddaden

No, it's false. Slice[1, 2, 3] allocates new memory for its contents. So it has a different pointer than slice.

Doh 🤦 It should be same? instead of ==. Fixed.

straight-shoota avatar Jun 17 '24 10:06 straight-shoota

IIUC this alone won't let you use Slices in a compare-by-identity Hash yet, right?

HertzDevil avatar Jun 19 '24 02:06 HertzDevil

Correct. Compare by identity is based on object id and falls back to ==.

straight-shoota avatar Jun 19 '24 05:06 straight-shoota

With https://github.com/crystal-lang/crystal/pull/14728 adding #same?, I started wondering whether perhaps #overlaps? would be a useful method. (do the memory regions intersect at all?). In C that is often an important distinction for some algorithms (for optimization vs safety) but don't know if there will be any significant use cases in Crystal

oprypin avatar Aug 07 '24 17:08 oprypin

Same for #includes? to know whether a pointer/slice is within another slice. That being said, I'm not sure there would be that many usages in Crystal in general, outside of low level stuff such as memory allocators.

ysbaddaden avatar Aug 18 '24 16:08 ysbaddaden