I'm developing a network application in Golang. I have a slice of IP addresses. Each time a request comes I use net.LookupIP(host)
to find out IP address of host which returns a slice of net.IP
. What is the best approach to compare these?
By the way in Python we have a set
data structure which makes above question so easy to resolve but what about Go?
With a "set"
Building our set
There is no builtin
Set
type in Go, but you can elegantly use amap[Type]bool
as a set, e.g.:Note: we exploited the fact that if a key is not in the map, indexing the map results in the zero value for the value type, which is
false
in case ofbool
, properly telling that the element is not in the map (set).Try it on the Go Playground.
Note #2: there are a few tricks to make the code to handle a map as a set shorter, you can check them in this answer: Check if a value is in a list.
Using
net.IP
in the setNow we only need a type representing a
net.IP
which can be used as the key type in a map (see this question about what constitutes a map key type: How can I prevent a type being used as a map key?).Unfortunately
net.IP
itself does not qualify, because it is a slice:And slices are not comparable. See this question for details: Hash with key as an array type and this: Why have arrays in Go?
An easy way is to convert it to a canonical
string
value and we're done. For this we may simply convert the bytes of the IP to a hexstring
. But an IPv4 address may be presented as IPv6, so we should first convert it to IPv6:Note: bytes of an IP address may not be a valid UTF-8 encoded
string
(which is how Go storesstring
s in memory), butstring
values in Go represent arbitrary byte sequences, so the following also works, is much simpler and is much more efficient:We can use such IP strings as the keys. Populate your map with IPs to check against:
If you have multiple IPs to check (as returned by
net.LookupIP()
), it's a singlefor
loop:Alternative Key type
Note that –as mentioned above– slices are not comparable but arrays are. So we could also use an array as the key. This is how it could look like:
Alternatives
Sorted slice
Alternatively we could just simply store the forbidden IPs in a slice
[]net.IP
, and keep it sorted. If it is sorted, we can use binary search to find an IP in it (standard librarysort.Search()
).Yes, binary search has
O(log2(n))
complexity compared to theO(1)
complexity of the (hash)map solution above. But this alternative has another pro:Enumerating individual IPs is not always practical. Sometimes (often) it is easier to list IP ranges. The first solution is not feasible to handle IP ranges, but this solution may be: you can find ranges that cover an IP address also in
O(log2(n))
time.You may use
func (ip IP) Equal(x IP) bool
fromnet
package:Like this working sample: