在Rust Programming Language一书的 Structs 一章中,我们将介绍tuple structs。
在哪些情况下我应该使用元组结构而不是正常的元组(除了书中提到的例子)?
答案 0 :(得分:13)
在以下情况下使用元组结构:
你会发现这种情况并非经常发生。如果是,您通常只是尝试将一个其他类型完全包装到新类型中,以使其具有不同的行为。这是一种已知的模式,称为'newtype'模式。如果结构中有多个字段,通常需要为它们命名。
快速提醒:
我们来看看[T]::split_at()
:
fn split_at(&self, mid: usize) -> (&[T], &[T])
他们这个函数的作者选择在这里返回一个元组。让我们看看,我们会从...获得任何东西吗?
SplitSlice
,SliceParts
,......?我们可以提供的每个名称都是多余的,因为该功能已经正确命名。left
和right
会更明确哪一方是哪一方。但在这里我们假设程序员有正确的直觉。英语是从左到右书写的,在英语文化中,数组通常是从左到右绘制的([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.0
或span.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_number
,number
,line
,... inner
,value
,data
,... 命名该领域并没有什么好处。所以,让我们不要使用......
⇒元组结构
决定使它成为一个自己的类型有一些很好的结果:我们可以在源代码中使用基于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),而且元组效果很好。
那么,何时使用元组结构?当你需要或希望命名一个类型时,你更喜欢使用元组结构而不是常规结构或枚举(完全是另一个讨论!)。