testing whether two maps are the same object

661 views
Skip to first unread message

Jochen Voss

unread,
Jul 18, 2023, 10:35:38 AM7/18/23
to golang-nuts
Dear all,

To implement the "eq" operator in a simple PostScript interpreter, I need to determine whether two maps are the same object.

This can be done by adding a new element to one map, and checking whether the new entry also appears in the second map, but as you can imagine, the resulting code is quite ugly.  See https://go.dev/play/p/AfembYDt3en for an implementation of this idea.

Is there a better way?

Many thanks,
Jochen

Jan Mercl

unread,
Jul 18, 2023, 10:45:18 AM7/18/23
to Jochen Voss, golang-nuts
On Tue, Jul 18, 2023 at 4:35 PM Jochen Voss <joche...@gmail.com> wrote:

> Is there a better way?

I have never been here and please don't do this:
https://go.dev/play/p/x4QYJubXMnQ

Jason Phillips

unread,
Jul 18, 2023, 10:52:24 AM7/18/23
to golang-nuts
You can also use the reflect package rather than (directly) reaching for unsafe. The reflect.DeepEqual function does something along the lines of: https://go.dev/play/p/IVt0Z-mxugh

Jochen Voss

unread,
Jul 18, 2023, 11:21:49 AM7/18/23
to golang-nuts
Dear Jason,

Thanks for the idea of using reflect.  Do you know whether `reflect.ValueOf(a).UnsafePointer()` is somehow "safer" than `*(*uintptr)(unsafe.Pointer(&a))`?

reflect.DeepEqual will not work, since it just compares contents of the maps: https://go.dev/play/p/tE_qZI2cKEd

All the best,
Jochen

Stephen Illingworth

unread,
Jul 18, 2023, 11:25:42 AM7/18/23
to golang-nuts

I like that. I think it's is quite a smart way of doing it,

I don't think you need to check both maps when choosing a key. Once you've found a candidate key in one map, you can test the other map and if it does exist then the two maps aren't equal. You've then saved the insertion and deletion step. I would also prefer to use rand.Int() rather than a linear search.

Jason Phillips

unread,
Jul 18, 2023, 11:44:53 AM7/18/23
to golang-nuts
reflect.Value.UnsafePointer() is probably not safer than using unsafe directly, assuming you're using unsafe in a way that doesn't break the rules. Reflect is doing effectively the same unsafe.Pointer conversion under the hood [1]. It's certainly easier on the eyes, in my opinion, though.

Jason Phillips

unread,
Jul 18, 2023, 11:52:51 AM7/18/23
to golang-nuts
Also note: reflect.DeepEqual doesn't just compare the contents of the map. It only compares contents if the maps aren't "the same map object". From the documentation:

> Map values are deeply equal when all of the following are true: they are both nil or both non-nil, they have the same length, and either they are the same map object or their corresponding keys (matched using Go equality) map to deeply equal values.

Comparing if they're "the same map object" is done in the way described earlier, which seems exactly what you're looking for.

Jochen Voss

unread,
Jul 18, 2023, 11:55:57 AM7/18/23
to golang-nuts
Thanks Jason, I get your point about DeepEqual now :)

Maxime Soulé

unread,
Jul 19, 2023, 3:21:21 AM7/19/23
to golang-nuts
Hi,

If you are in tests context, you can have a look at go-testdeep [1] and its Shallow operator [2] like in: https://go.dev/play/p/kNItQmDOJDy

Regards,

Max.

long văn

unread,
Jul 19, 2023, 8:14:28 AM7/19/23
to golang-nuts

Why dont you turn that 2 maps into json then compare its diffrent or not
Vào lúc 21:35:38 UTC+7 ngày Thứ Ba, 18 tháng 7, 2023, Jochen Voss đã viết:

Axel Wagner

unread,
Jul 19, 2023, 9:11:25 AM7/19/23
to Jochen Voss, golang-nuts
On Tue, Jul 18, 2023 at 5:22 PM Jochen Voss <joche...@gmail.com> wrote:
Dear Jason,

Thanks for the idea of using reflect.  Do you know whether `reflect.ValueOf(a).UnsafePointer()` is somehow "safer" than `*(*uintptr)(unsafe.Pointer(&a))`?

For what it's worth: I'd argue comparing `reflect.ValueOf(a).UnsafePointer()` provides stronger guarantees of portability and correctness than comparing `*(*uintptr)(unsafe.Pointer(&a))`. Also, you should probably use `*(*unsafe.Pointer)(unsafe.Pointer(&a))` if anything. It does the same thing, when it works, but I'm not sure that the conversion to `uintptr` that is happening is guaranteed to work under a moving GC. A comparison isn't necessarily arithmetic - and those rules are just what `gc` is specifying anyways, an implementation is free to choose others, in theory.

That's because the latter relies on maps being "pointer-shaped". In theory, an implementation might not represent a map as a plain pointer. Though I admittedly having trouble coming up with an alternative design.

Plus, the documentation of `reflect.Value.UnsafePointer` *also* doesn't really say *what* the returned pointer is for a map and what its semantics are. But it seems somewhat safer to assume that an implementation that would *not* represent a map as a plain pointer, would implement `reflect.Value.UnsafePointer` in a way that still allowed you to compare them for identity purposes.

But, as you might be able to tell, all of this is a bit of a nitpick of how willing you are to make assumptions about reasonable ways an implementation might deviate from what they *have* to do. I'm pretty sure there is no way to compare map values that is keeping *strictly* in the bounds of well-defined behavior. I would, personally, feel comfortable using either and to just assume that I never have to run my code in a sufficiently wild implementation. And I'd probably try to benchmark/test if the `reflect` based version changes performance characteristics (I would assume it makes it easier for the map to escape, but maybe maps *always* escape anyways) and if not, use `reflect`.


reflect.DeepEqual will not work, since it just compares contents of the maps: https://go.dev/play/p/tE_qZI2cKEd

All the best,
Jochen
On Tuesday, 18 July 2023 at 15:52:24 UTC+1 Jason Phillips wrote:
You can also use the reflect package rather than (directly) reaching for unsafe. The reflect.DeepEqual function does something along the lines of: https://go.dev/play/p/IVt0Z-mxugh

On Tuesday, July 18, 2023 at 10:45:18 AM UTC-4 Jan Mercl wrote:
On Tue, Jul 18, 2023 at 4:35 PM Jochen Voss <joche...@gmail.com> wrote:

> Is there a better way?

I have never been here and please don't do this:
https://go.dev/play/p/x4QYJubXMnQ

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/30202b52-554e-462e-aaa4-dfe7f4d23554n%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages