什么时候我应该使用元组结构超过正常元组?

时间:2016-10-16 11:50:25

标签: types rust

在Rust Programming Language一书的 Structs 一章中,我们将介绍tuple structs

在哪些情况下我应该使用元组结构而不是正常的元组(除了书中提到的例子)?

3 个答案:

答案 0 :(得分:13)

摘要

在以下情况下使用元组结构

  • 类型的名称带有语义信息
  • 命名字段不会添加任何语义信息

你会发现这种情况并非经常发生。如果是,您通常只是尝试将一个其他类型完全包装到新类型中,以使其具有不同的行为。这是一种已知的模式,称为'newtype'模式。如果结构中有多个字段,通常需要为它们命名。

实施例

快速提醒:

  • 元组是具有匿名字段的匿名类型
  • 元组结构是具有匿名字段的命名类型
  • 结构是具有命名字段的命名类型

我们来看看[T]::split_at()

fn split_at(&self, mid: usize) -> (&[T], &[T])

他们这个函数的作者选择在这里返回一个元组。让我们看看,我们会从...获得任何东西吗?

  • 命名类型:不是,对吧?我们称之为什么? SplitSliceSliceParts,......?我们可以提供的每个名称都是多余的,因为该功能已经正确命名。
  • 命名字段:有问题。 leftright会更明确哪一方是哪一方。但在这里我们假设程序员有正确的直觉。英语是从左到右书写的,在英语文化中,数组通常是从左到右绘制的([0 | 1 | 2 | 3 ]),所以对大多数人来说都是有意义的。

⇒只是一个元组很好!

另一个例子:假设你正在编写一个以某种方式与文本文件(编译器,文本编辑器......)一起工作的应用程序。当您在讨论文本文件中的某个区域(例如,搜索结果)时,您希望通过提供字节偏移来指定该区域。让我们看一下元组是否适合我们:

fn find_first_occurence(file: &TextFile, needle: &str) -> (usize, usize)

回报类型是否适合自己?而不是......即使您知道文件中的区域是由字节偏移指定的,返回值仍然不明确:它是(start, end)还是(start, stuff),其中的东西可以是任何其他搜索指标(该函数不需要返回end,因为我们已经知道needle的长度,因此可以计算它。所以,我希望你同意,我们想要命名返回类型。我们称之为Span - 这是使用的名称in the Rust compiler

下一个问题:struct或tuple struct?命名字段是否有意义?同样,没有明确的答案,但我认为我们想要命名字段。什么更容易阅读:span.1 - span.0span.high - span.low?另外,我们可以为命名字段编写文档;例如,记录high是独占的。

⇒struct

现在想象您想要向用户报告行号。特别重要的是返回给定跨度的相应行号的函数(为简单起见,我们假设此跨度从不跨越多行)。

fn get_line_number(file: &TextFile, span: Span) -> ???

那么我们又回来了什么?在这个函数的上下文中,一个简单的u32可能很好!毫无疑问,u32代表什么。虽然:行号计数是从0还是1开始? 叹息

当然,我们可以在函数上记录这个属性......并且每个其他函数都使用行号。那么如何创建一个新类型并将其记录在那里呢?这也有助于获取多个数字的函数,包括行号:

print_snippet(&file, 57, 63, 80);

等等,现在的行号是什么?确切地说:代替u32 s,需要LineNumber s - 类型系统是文档。

我们现在同意创建一种新类型。但是:struct或tuple struct?我们来试试struct:

struct LineNumber {
    line_number: u32,    // uhm...
}

那么,怎么称呼这个领域?唯一合适的名称分为两类:

  • 喜欢结构名称:line_numbernumberline,...
  • 没有语义信息:innervaluedata,...

命名该领域并没有什么好处。所以,让我们不要使用......

⇒元组结构

决定使它成为一个自己的类型有一些很好的结果:我们可以在源代码中使用基于0的数字,但永远不必担心错误地打印它:

impl fmt::Display for LineNumber {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        (self.0 + 1).fmt(f)
    }
}

我们可以在一个地方将基于0的数字调整为基于1的数字(对于那些该死的人类!)!

另请注意,我们之前讨论的是字节偏移。我们还应该创建一个新类型来区别于char偏移,而不是使用usize,而不是使用{{1}}。

答案 1 :(得分:2)

元组结构不太常见。当你只有几个成员时使用它们,并且它们足够清楚,它们不需要名称。

元组结构的一个常见用法是 newtypes 。这是一个只有一个成员的元组结构。这对于围绕现有类型进行简单包装非常有用。

答案 2 :(得分:2)

元组和元组结构之间的主要区别在于后者引入了名称,而前者没有。

有时,数据片段捆绑在一起只是因为。例如,假设一个状态机:每个转换返回一个数据以及状态机的下一个状态,这是什么名字?

impl StateA {
    fn on_event_x(self) -> (String, StateB);
}

这不应该被滥用;毕竟,名称对于记录API非常有用!然而,有时候,没有愚蠢的名字(StringAndStateB:x),而且元组效果很好。

那么,何时使用元组结构?当你需要或希望命名一个类型时,你更喜欢使用元组结构而不是常规结构或枚举(完全是另一个讨论!)。