「这是我参与11月更文挑战的第 19 天,活动详情查看:2021最后一次更文挑战」
Example: PacketType
让我们举一个不同的例子。假设我们现在正在为一个网络协议实现一个库,其中数据包头的第一个字节告诉我们数据包的类型。一个合理的解决方案是用一个枚举来表示数据包类型,其中每个变量都映射到一个数据包类型。比如说:
1 | rust复制代码/// Represents a packet type. |
考虑到这种表示法,我们应该如何转换为字节和从字节转换出去?
传统的方法,在C和C++程序中非常常见,就是简单地将数值从一种类型转换为另一种类型。这在Rust中也可以做到;例如,将 PacketType::Data
转换为字节,就像 PacketType::Data
转换为u8一样简单。这似乎已经解决了将PacketType编码为字节表示的问题,但我们还没有完成。
你注意到每个PacketType变量都有一个相关的值吗?它们定义了变体在生成的代码中的表示。如果我们遵循通常的Rust风格,不给变量分配任何值,那么每个变体的数字表示将取决于它们被声明的顺序,如果我们简单地将枚举变体转换成数字类型,就会导致错误。将枚举变体转换为正确值的一个更好的方法是显式匹配。
1 | rust复制代码impl From<PacketType> for u8 { |
很直接,对吧!由于从PacketType到u8的映射包含在From的实现中,我们可以删除分配给PacketType变体的值,从而得到一个更简洁的枚举定义。
相反的转换?
根据常见问题,将枚举转换为整数可以通过cast实现,正如我们看到的那样。然而,相反的转换可以(而且我认为,在很多情况下,应该)用匹配语句来实现。为了便于使用和更好的工程学,为两个(对称)方向的转换实现 From<T>
通常是个好主意。
将一个 PacketType 转换成 u8 通常是安全和正确的,除了我们之前看到的注意事项,因为对于每个 PacketType 变量,都有一个与 u8 兼容的对应表示。然而,反过来说就不对了:在没有相应的 PacketType 变量的情况下转换u8值是未定义的行为。
尽管我们可以将任何 PacketType 变量映射到u8值中,但我们不能反过来将任何u8映射到PacketType中:u8太多,而 PacketType 不够用!所以对于u8到PacketType的转换,我们不能简单地匹配u8值,并返回相应的PacketType变量。
因此,对于u8到PacketType的转换,我们不能简单地匹配u8值并返回适当的PacketType变体,就像我们对反向转换所做的那样。我们需要一种方法来提示转换失败,但调用panic!()并不是一个可接受的选择。我们需要一个易犯错误的From。
本文转载自: 掘金