What's "unique" in Go std

What's "unique" in Go std

Go v1.23 has been released just yet. With iterators being the most discussed feature there are couple more changes. Today I made a tiny experiments with the freshly baked unique library, that is now a part of std.

What problem is it addressing?

It is simple and short: comparison. The unique is working with comparable interface and it is here to improve the way we compare what's comparable. In reality it might be easy to compare ints, floats and strings and do not even consider any improvement, however comparing more complex data might not become trivial.

Let's consider a couple of issues: we have a model (or DTO) that represents a bunch of data. Let's assume it is stored in DB and obviously it has a unique identifier. If we would need to know whether the fetched user record is the same user that is logged in it would be relatively easy: compare the IDs (or whatever you call a unique identifier) and make a decision.

However, what happens if we need to determine, whether User struct was changed. We could implement complex handlers to update the logic and somehow keep the state of object being updated or not, but this is smelly solution. The most straightforward way to go is just comparing 2 structs. If these are not the same, but IDs are equal it means we are working on altered version of the struct.

While we can compare two structs directly most of the time this is not the way we want to go for the most of the time. We might want to ignore some properties, there are might be too many properties and it may feel not right to evaluate and compare all of them. And this is exactly the problem that is addressed with unique.

Experiments

I created a simple User struct and created 3 instances. I am running comparison on the loop, to get a meaningful results (10_000_000 iterations if you are curious). Spoiler alert: there is a significant difference, so I do not see a reason to establish some dedicated environment and run the test another million times to make sure results are not affected by other conditions.

type User struct {
	ID       uuid.UUID
	Name     string
	Age      int
	LastName string
	Comment  string
	Comment2 string
}

Here is the code

	id := uuid.New()

	user1 := User{
		ID:       id,
		Name:     "John",
		Age:      25,
		LastName: "Doe",
		Comment2: "Hello, this is a string",
	}

	user2 := User{
		ID:       id,
		Name:     "John",
		Age:      25,
		LastName: "Doe",
		Comment:  "Hello, this is a string",
		Comment2: "Hello, this is a string",
	}

	user3 := User{
		ID:       uuid.New(),
		Name:     "John",
		Age:      25,
		LastName: "Doe",
		Comment:  "Hello, this is a string",
		Comment2: "Hello, this is a string",
	}

user1 is compared to user2 and user3 later

The results

First of all: it turned out it matters a lot whether structure's properties are the same. It seems that if the properties are different we get a better (faster) comparison approach.

With the code above I am getting stable 30-40 ms. As soon as I add Comment property to the User1 the time increases twice (70-80 ms). First outcome is clear: if structs has different properties they are compared faster. However, introducing more properties and making each User unique does not reduce the time below the initial 30-40ms.

The second part: using the unique package. It is as simple wrapping the structs with a unique.Make() call

	u1 := unique.Make(user1)
	u2 := unique.Make(user2)
	u3 := unique.Make(user3)

Now when comparing u1, u2 and u3 instead of struct values gives stable 3.3 - 3.5 ms. This is the 10x difference with the base (most optimal) scenario, when structs are obviously unequal (different set of properties has been set). The coolest part is: comparison time is not growing no matter how structs are shaped.

Conclusions

Simple tests shows that struct comparison is already optimised, but the unique package introduces another concept of comparing the complex object that drastically reduces the time spent on it. This might not be a real life improvement comparing strings or other simple cases, when direct comparison is good. Nevertheless comparison of complex structs, that might have many levels and especially when these are shaped the same way (the same properties are set) might get an amazing performance boost with these changes.

Keep GO-ing!