IP-Part of net.IPNet does not fulfil reflect.DeepEqual, but are Equal

huangapple go评论67阅读模式
英文:

IP-Part of net.IPNet does not fulfil reflect.DeepEqual, but are Equal

问题

我正在经历一个单元测试中的意外行为,该测试使用reflect.DeepEqual来测试两个解析结构的相等性。这些结构包含一个net.IPNet结构的切片。测试失败是因为reflect.DeepEqual返回false。

在追踪此问题时,我发现了DeepEqual的以下意外行为。对我来说最有趣的是它可以处理IPv6地址,并且使用解析后的IP,而不是net.IPNet结构中的掩码IP。

有人能解释一下:

  • 为什么这些IPv4地址不相等,尽管它们的字节表示似乎相等?
  • 为什么IPv6地址相等?
  • 如何构造一个与使用ParseCIDR生成的net.IP实例相匹配的实例,以满足DeepEqual的要求?

以下是可用go run运行的示例程序:

package main

import (
	"fmt"
	"net"
	"reflect"
)

func main() {
	aip, a, _ := net.ParseCIDR("135.104.0.0/32")
	//aip, a, _ := net.ParseCIDR("abcd:2345::/65")

	bip := net.IPv4(135, 104, 0, 0)
	//bip := net.ParseIP("abcd:2345::")


	// IPa: 135.104.0.0 3133352e3130342e302e30
	fmt.Printf("IPa: %s %x\n", a.IP, a.IP)

	// IPb: 135.104.0.0 3133352e3130342e302e30
	fmt.Printf("IPb: %s %x\n", bip, bip)

	fmt.Println("eq?:", a.IP.Equal(bip)) // true

	// I'd expect this to be true
	fmt.Println("deep eq?:", reflect.DeepEqual(a.IP, bip)) // false

	fmt.Println("deep eq w/o mask?:", reflect.DeepEqual(aip, bip)) // true
}

希望对你有所帮助!

英文:

I'm experiencing unexpected behavior in a unit test which uses reflect.DeepEqual to test for the equality of two parsed structures. The structures contain a Slice of net.IPNet structs. The tests fail because reflect.DeepEqual returns false.

While tracking this issue down I found the following unexpected behavior of DeepEqual. The most fascinating thing for me is that it works with IPv6 addresses and using the war parsed IP, not the masked one in the net.IPNet struct.

Can anyone explain to me:

  • Why are those IPv4 addresses not DeepEqual, despite their byte-representation seems to be?
  • Why are they DeepEqual for IPv6 addresses?
  • How to construct an net.IP instance that match the one generated by ParseCIDR with respect to DeepEqual?

Example program, runable with go run:

package main

import (
	"fmt"
	"net"
	"reflect"
)

func main() {
	aip, a, _ := net.ParseCIDR("135.104.0.0/32")
	//aip, a, _ := net.ParseCIDR("abcd:2345::/65")

	bip := net.IPv4(135, 104, 0, 0)
	//bip := net.ParseIP("abcd:2345::")


	// IPa: 135.104.0.0 3133352e3130342e302e30
	fmt.Printf("IPa: %s %x\n", a.IP, a.IP)

	// IPb: 135.104.0.0 3133352e3130342e302e30
	fmt.Printf("IPb: %s %x\n", bip, bip)

	fmt.Println("eq?:", a.IP.Equal(bip)) // true

	// I'd expect this to be true
	fmt.Println("deep eq?:", reflect.DeepEqual(a.IP, bip)) // false

	fmt.Println("deep eq w/o mask?:", reflect.DeepEqual(aip, bip)) // true
}

答案1

得分: 1

它们不是深度相等的,因为它们不符合深度相等的定义

通过查看每个值的内部表示,就像我在这个playground上所做的那样,我们可以看到虽然它们都是IPv4地址,但bip前面有一些额外的字节(可能是IPv6表示...为什么,我不确定)。

IPa: 135.104.0.0 3133352e3130342e302e30 net.IP net.IP{0x87, 0x68, 0x0, 0x0}
IPb: 135.104.0.0 3133352e3130342e302e30 net.IP net.IP{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x87, 0x68, 0x0, 0x0}

此外,使用DeepEqual来比较像IP地址这样基本的东西是一个非常糟糕的主意,因为IP地址对于相等目的来说是简单的不透明值。所以直接比较这些不透明值。这意味着从各种存储结构中输出一些内容,但以一种通用的格式输出它们并进行比较。直接比较字节表示(int32)的IP地址通常是最高效的。如果需要比较网络地址、子网掩码等,请以相同的方式进行。或者,如果对于你的工具来说更容易,可以比较字符串表示。

但在核心,IP地址只是一个32位整数(对于IPv6来说是128位整数),其他属性是其他整数。对整数进行深度相等性检查是没有意义的。

英文:

They aren't deep equal because they don't meet the definition of deep equality.

By looking at the internal representation of each value, as I did on this playground, we can see that while they are both IPv4 address, bip is preceded by a number of extra bytes (presumably the IPv6 representation... why, I'm not sure).

IPa: 135.104.0.0 3133352e3130342e302e30 net.IP net.IP{0x87, 0x68, 0x0, 0x0}
IPb: 135.104.0.0 3133352e3130342e302e30 net.IP net.IP{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x87, 0x68, 0x0, 0x0}

Further, using DeepEqual to compare something as basic as an IP address is a Very Bad Idea™, for the simple reason that IPs are not deep structures. They are (for equality purposes) simple opaque values. So just compare the opaque values directly. This will mean outputting something from your various storage structs, but output them in a common format and compare that. Comparing straight IPs in their byte representation (int32) will generally be the most efficient. If you need to compare the network address, netmask, etc, do it the same way. Alternately, compare a string representation, if that's easier for your tools.

But at the core, an IP address is just a 32-bit integer (or 128-bit integer in the case of IPv6), and the other attributes are other integers. Checking for Deep Equality on integers makes no sense.

huangapple
  • 本文由 发表于 2017年3月21日 15:58:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/42921227.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定