在 Sway 中,结构体是一组命名的类型。您可能也通过另一个名称熟悉结构体:产品类型。Sway 并没有对结构体进行显著独特的用法;它们与大多数其他具有结构体的语言类似。如果您来自面向对象的背景,那么结构体就像对象的数据属性。
这些数据属性称为 字段,可以是公共的或私有的。
私有结构体字段只能在其结构体声明所在的模块内访问。公共字段则可在结构体可访问的任何地方访问。字段级别的访问控制允许更精细地封装数据。
为了解释这些概念,让我们看一下以下示例,在该示例中我们有一个名为 data_structures 的模块。
在该模块中,我们声明了一个名为 Foo
的结构体,它有两个字段。第一个字段名为 bar
,它是公共的,并接受 u64
类型的值。第二个字段名为 baz
,它也是公共的,并接受 bool
类型的值。
类似地,我们定义了结构体 Point
、Line
和 TupleInStruct
。由于所有这些结构体都是公共的,并且它们的所有字段都是公共的,因此可以在其他模块中使用 结构体实例化语法 来实例化它们,如下所示。
另一方面,结构体 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;
}
注意 您可以同时混合和匹配这三种实例化结构体的方式。 此外,在实例化时字段的顺序并不重要,但我们鼓励按字母顺序声明字段,并以相同的字母顺序实例化它们。
此外,可以使用解构语法从结构体中提取多个变量。
注意 如果您是新手或者对编程不太了解,这些信息并不是必需的
结构体没有内存开销。这意味着在内存中,每个结构体字段都是顺序排列的。运行时不会保留关于结构体名称或其他属性的元数据。换句话说,结构体是编译时构造。这与 Rust 中的情况相同,但与其他具有运行时的语言不同,如 Java。
元组是一种基本的静态长度类型 ,它们包含多个不同类型的值。元组的类型由其中的值的类型定义,元组可以包含基本类型以及结构体和枚举。
您可以通过使用 .
语法直接访问值。此外,可以使用解构语法从元组中提取多个变量。
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));
}
枚举,或 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;
}
枚举变体也可以包含额外的数据。看一下这个更实质性的示例,它将结构体声明与枚举变体结合起来:
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,
});
}
可以定义枚举的枚举:
library;
pub enum Error {
StateError: StateError,
UserError: UserError,
}
pub enum StateError {
Void: (),
Pending: (),
Completed: (),
}
pub enum UserError {
InsufficientPermissions: (),
Unauthorized: (),
}
使用个别(非嵌套)枚举直接使用是首选的,因为它们易于跟踪,并且行数较短:
library;
use ::enum_of_enums::{StateError, UserError};
fn preferred() {
let error1 = StateError::Void;
let error2 = UserError::Unauthorized;
}
如果您希望使用上面示例中的 Error
枚举的嵌套形式,则可以使用以下语法将其实例化为变量:
library;
use ::enum_of_enums::{Error, StateError, UserError};
fn avoid() {
let error1 = Error::StateError(StateError::Void);
let error2 = Error::UserError(UserError::Unauthorized);
}
需要注意的要点:
Error
枚举 注意 如果您是新手或者对编程不太了解,这些信息并不是必需的。
枚举确实会有一些内存开销。为了知道表示的是哪个变体,Sway 为枚举变体存储了一个字(8 个字节)的标签。标签后保留的空间等于 最大 枚举变体的大小。因此,要计算枚举在内存中的大小,请将最大变体的大小加上 8 字节。例如,在上面的 Color
中,其中的变体都是 ()
,大小为 0 字节,因此大小为 8 字节。