Rust 中的字节序、API 设计和多态性 - Jimmy


我最近一直在做一个涉及字节序(也称为字节顺序)的序列化项目,它让我探索了 Rust 标准库中处理字节序的部分,并分享了我对如何在编程中表示字节序的想法语言及其标准库,因为我认为这也是 Rust 比 C++ 做得更好的地方,并且也是一个很好的案例研究来讨论 Rust 中的 API 设计和多态性。
 
Big End, Little End
当我第一次在 DOS 上学习使用DEBUG.EXE程序进行编程时,我第一次遇到了字节序的概念。当一个16位值显示为16位值时,它只是正常的十六进制,但是当它显示为两个8位字节时,显示就发生了奇怪的事情。
这是一个演示效果的C++ 片段

template<typename T>
void display_bytes(const T &val) {
    char bytes[sizeof(T)];
    memcpy(bytes, &val, sizeof(T));
    for (auto byte : bytes) {
        printf("%2x ", byte);
    }
    printf(
"\n");
}

int main() {
    int value = 0x12345678;
    printf(
"%x\n", value);
    display_bytes(value);
}

当在任何小端Little End处理器(绝大多数处理器)上运行时,我们得到:

12345678
78 56 34 12

最低有效字节在前,因此如果您按顺序打印出各个字节,则必须向后读取它——尽管每个单独的字节仍然是向前的。
如果您在大端Big End,处理器上运行相同的代码,您将得到:
12345678
12 34 56 78

在这一点上,我理解的小字节序是英特尔处理器出于我不理解的原因所做的一件奇怪的事情,这让我在阅读十六进制转储时做了一些额外的工作。当时,这很好:我认为必须应用这些额外的奥术知识本身就很酷。而且,我认为小字节序是做需要额外工作的事情的奇怪方式,而大字节序是更自然的设计。直到很久以后,我才对这个观点有了一些细微的差别。
看,我们用于数字的书写系统是大端。我们不是将数字划分为字节(以 256 为基数),而是将其划分为数字。我们认为页面左侧在页面右侧之前,我们首先写入最高有效数字。这都是在小学明确教授的:

1234 = 1*10^3 + 2*10^2 + 3*10^1 + 4*10^0

。。。。。
 
字节序何时相关?
在书写数字时,数字没有字节序,同样,一个字节在处理器中是不可分割的。字节由位组成,但在特殊指令之外,这些位的顺序无关紧要。其中一个是最重要的,一个是最不重要的,但是除非我们为特殊指令索引它们,或者通过电线一个接一个地发送它们,否则无法确定哪个位是“第一个”。
我认为谈论(多字节)字本身的字节序没有任何意义。单词的字节序只有在它被存储为——并且可以作为——一系列字节访问时才起作用。
那么从这个角度来看,与字节序相关的操作是什么?给定一个词,它由哪一系列字节组成?然后,给定一系列字节,它是什么词?
在 Rust 的术语中,这些是to_be_bytes(对于大端)/ to_le_bytes(对于小)在一个方向,而from_be_bytes/from_le_bytes在另一个方向。
这些方法都捆绑在 Rust 文档中, 用于——在这种情况下——原始u32类型,以及ne处理器的本机字节序。
。。
更多点击标题