Structs Tuples and Enums

Icon Link结构体、元组和枚举

Icon Link结构体

在 Sway 中,结构体是一组命名的类型。您可能也通过另一个名称熟悉结构体:产品类型。Sway 并没有对结构体进行显著独特的用法;它们与大多数其他具有结构体的语言类似。如果您来自面向对象的背景,那么结构体就像对象的数据属性。

这些数据属性称为 字段,可以是公共的或私有的。

私有结构体字段只能在其结构体声明所在的模块内访问。公共字段则可在结构体可访问的任何地方访问。字段级别的访问控制允许更精细地封装数据。

为了解释这些概念,让我们看一下以下示例,在该示例中我们有一个名为 data_structures 的模块。

在该模块中,我们声明了一个名为 Foo 的结构体,它有两个字段。第一个字段名为 bar,它是公共的,并接受 u64 类型的值。第二个字段名为 baz,它也是公共的,并接受 bool 类型的值。

类似地,我们定义了结构体 PointLineTupleInStruct。由于所有这些结构体都是公共的,并且它们的所有字段都是公共的,因此可以在其他模块中使用 结构体实例化语法 来实例化它们,如下所示。

另一方面,结构体 StructWithPrivateFields 只能在 data_structures 模块内实例化,因为它包含私有字段。为了能够在声明它们的模块之外创建此类结构体的实例,结构体必须提供 构造函数关联函数

// the _data_structures_ module
library;
 
// Declare a struct type
pub struct Foo {
    pub bar: u64,
    pub baz: bool,
}
 
// Struct types for destructuring
pub struct Point {
    pub x: u64,
    pub y: u64,
}
 
pub struct Line {
    pub p1: Point,
    pub p2: Point,
}
 
pub struct TupleInStruct {
    pub nested_tuple: (u64, (u32, (bool, str))),
}
 
// Struct type instantiable only in the module _data_structures_
pub struct StructWithPrivateFields {
    pub public_field: u64,
    private_field: u64,
    other_private_field: u64,
}
 

为了实例化结构体,我们使用了 结构体实例化语法,它与声明语法非常相似,只是用表达式代替了类型。

有三种实例化结构体的方法。

  • 为字段硬编码值
  • 传递变量,其名称与结构体字段不同
  • 使用变量名称与字段名称相同的简写符号
library;
 
mod data_structures;
use data_structures::{Foo, Line, Point, TupleInStruct};
 
fn hardcoded_instantiation() -> Foo {
    // Instantiate `foo` as `Foo`
    let mut foo = Foo {
        bar: 42,
        baz: false,
    };
 
    // Access and write to "baz"
    foo.baz = true;
 
    // Return the struct
    foo
}
 
fn variable_instantiation() -> Foo {
    // Declare variables with the same names as the fields in `Foo`
    let number = 42;
    let truthness = false;
 
    // Instantiate `foo` as `Foo`
    let mut foo = Foo {
        bar: number,
        baz: truthness,
    };
 
    // Access and write to "baz"
    foo.baz = true;
 
    // Return the struct
    foo
}
 
fn shorthand_instantiation() -> Foo {
    // Declare variables with the same names as the fields in `Foo`
    let bar = 42;
    let baz = false;
 
    // Instantiate `foo` as `Foo`
    let mut foo = Foo { bar, baz };
 
    // Access and write to "baz"
    foo.baz = true;
 
    // Return the struct
    foo
}
 
fn struct_destructuring() {
    let point1 = Point { x: 0, y: 0 };
    // Destructure the values from the struct into variables
    let Point { x, y } = point1;
 
    let point2 = Point { x: 1, y: 1 };
    // If you do not care about specific struct fields then use ".." at the end of your variable list
    let Point { x, .. } = point2;
 
    let line = Line {
        p1: point1,
        p2: point2,
    };
    // Destructure the values from the nested structs into variables
    let Line {
        p1: Point { x: x0, y: y0 },
        p2: Point { x: x1, y: y1 },
    } = line;
    // You may also destructure tuples nested in structs and structs nested in tuples
    let tuple_in_struct = TupleInStruct {
        nested_tuple: (42u64, (42u32, (true, "ok"))),
    };
    let TupleInStruct {
        nested_tuple: (a, (b, (c, d))),
    } = tuple_in_struct;
 
    let struct_in_tuple = (Point { x: 2, y: 4 }, Point { x: 3, y: 6 });
    let (Point { x: x0, y: y0 }, Point { x: x1, y: y1 }) = struct_in_tuple;
}
 
Icon InfoCircle

注意 您可以同时混合和匹配这三种实例化结构体的方式。 此外,在实例化时字段的顺序并不重要,但我们鼓励按字母顺序声明字段,并以相同的字母顺序实例化它们。

此外,可以使用解构语法从结构体中提取多个变量。

Icon Link结构体内存布局

Icon InfoCircle

注意 如果您是新手或者对编程不太了解,这些信息并不是必需的

结构体没有内存开销。这意味着在内存中,每个结构体字段都是顺序排列的。运行时不会保留关于结构体名称或其他属性的元数据。换句话说,结构体是编译时构造。这与 Rust 中的情况相同,但与其他具有运行时的语言不同,如 Java。

Icon Link元组

元组是一种基本的静态长度类型 ,它们包含多个不同类型的值。元组的类型由其中的值的类型定义,元组可以包含基本类型以及结构体和枚举。

您可以通过使用 . 语法直接访问值。此外,可以使用解构语法从元组中提取多个变量。

library;
 
fn tuple() {
    // You can declare the types yourself
    let tuple1: (u8, bool, u64) = (100, false, 10000);
 
    // Or have the types be inferred
    let mut tuple2 = (5, true, ("Sway", 8));
 
    // Retrieve values from tuples
    let number = tuple1.0;
    let sway = tuple2.2.1;
 
    // Destructure the values from the tuple into variables
    let (n1, truthness, n2) = tuple1;
 
    // If you do not care about specific values then use "_"
    let (_, truthness, _) = tuple2;
 
    // Internally mutate the tuple
    tuple2.1 = false;
 
    // Or change the values all at once (must keep the same data types)
    tuple2 = (9, false, ("Fuel", 99));
}
 

Icon Link枚举

枚举,或 enums,也称为 求和类型。枚举是一种可以是几种变体中的一种的类型。要声明枚举,您需要列出所有可能的变体。

在这里,我们定义了五种潜在的颜色。每个枚举变体只是颜色名称。由于每个变体都没有额外的数据关联,我们说每个变体的类型是 (),或单位。

library;
 
// Declare the enum
enum Color {
    Blue: (),
    Green: (),
    Red: (),
    Silver: (),
    Grey: (),
}
 
fn main() {
    // To instantiate a variable with the value of an enum the syntax is
    let blue = Color::Blue;
    let silver = Color::Silver;
}
 

Icon Link包含结构体的枚举

枚举变体也可以包含额外的数据。看一下这个更实质性的示例,它将结构体声明与枚举变体结合起来:

library;
 
struct Item {
    price: u64,
    amount: u64,
    id: u64,
}
 
enum MyEnum {
    Item: Item,
}
 
fn main() {
    let my_enum = MyEnum::Item(Item {
        price: 5,
        amount: 2,
        id: 42,
    });
}
 

Icon Link枚举的枚举

可以定义枚举的枚举:

library;
 
pub enum Error {
    StateError: StateError,
    UserError: UserError,
}
 
pub enum StateError {
    Void: (),
    Pending: (),
    Completed: (),
}
 
pub enum UserError {
    InsufficientPermissions: (),
    Unauthorized: (),
}
 

Icon Link首选用法

使用个别(非嵌套)枚举直接使用是首选的,因为它们易于跟踪,并且行数较短:

library;
 
use ::enum_of_enums::{StateError, UserError};
 
fn preferred() {
    let error1 = StateError::Void;
    let error2 = UserError::Unauthorized;
}
 

Icon Link不建议的用法

如果您希望使用上面示例中的 Error 枚举的嵌套形式,则可以使用以下语法将其实例化为变量:

library;
 
use ::enum_of_enums::{Error, StateError, UserError};
 
fn avoid() {
    let error1 = Error::StateError(StateError::Void);
    let error2 = Error::UserError(UserError::Unauthorized);
}
 

需要注意的要点:

  • 您必须导入所有需要的枚举,而不仅仅是 Error 枚举
  • 行可能会变得不必要地长(取决于名称)
  • 语法不是最符合人体工程学的

Icon Link枚举内存布局

Icon InfoCircle

注意 如果您是新手或者对编程不太了解,这些信息并不是必需的。

枚举确实会有一些内存开销。为了知道表示的是哪个变体,Sway 为枚举变体存储了一个字(8 个字节)的标签。标签后保留的空间等于 最大 枚举变体的大小。因此,要计算枚举在内存中的大小,请将最大变体的大小加上 8 字节。例如,在上面的 Color 中,其中的变体都是 (),大小为 0 字节,因此大小为 8 字节。