• 结构体
    • 更新语法(Update syntax)
    • 元组结构体
    • 类单元结构体(Unit-like structs)

    结构体

    structs.md


    commit 59e5e65270259666422e51721cc42f261f827386

    结构体是一个创建更复杂数据类型的方法。例如,如果我们正在进行涉及到 2D 空间坐标的计算,我们将需要一个x和一个y值:

    1. let origin_x = 0;
    2. let origin_y = 0;

    结构体让我们组合它们俩为一个单独,统一的数据类型:

    1. struct Point {
    2. x: i32,
    3. y: i32,
    4. }
    5. fn main() {
    6. let origin = Point { x: 0, y: 0 }; // origin: Point
    7. println!("The origin is at ({}, {})", origin.x, origin.y);
    8. }

    这里有许多细节,让我们分开说。我们使用了struct关键字后跟名字来定义了一个结构体。根据传统,结构体使用大写字母开头并且使用驼峰命名法:PointInSpace而不要写成Point_In_Space

    像往常一样我们用let创建了一个结构体的实例,不过我们用key: value语法设置了每个字段。这里顺序不必和声明的时候一致。

    最后,因为每个字段都有名字,我们可以访问字段通过圆点记法:origin.x

    结构体中的值默认是不可变的,就像 Rust 中其它的绑定一样。使用mut使其可变:

    1. struct Point {
    2. x: i32,
    3. y: i32,
    4. }
    5. fn main() {
    6. let mut point = Point { x: 0, y: 0 };
    7. point.x = 5;
    8. println!("The point is at ({}, {})", point.x, point.y);
    9. }

    上面的代码会打印The point is at (5, 0)

    Rust 在语言级别不支持字段可变性,所以你不能像这么写:

    1. struct Point {
    2. mut x: i32, // This causes an error.
    3. y: i32,
    4. }

    可变性是绑定的一个属性,不是结构体自身的。如果你习惯于字段级别的可变性,这开始可能看起来有点奇怪,不过这样明显地简化了问题。它甚至可以让你使变量只可变一段临时时间:

    1. struct Point {
    2. x: i32,
    3. y: i32,
    4. }
    5. fn main() {
    6. let mut point = Point { x: 0, y: 0 };
    7. point.x = 5;
    8. let point = point; // `point` is now immutable.
    9. point.y = 6; // This causes an error.
    10. }

    你的结构体仍然可以包含&mut指针,它会给你一些类型的可变性:

    1. struct Point {
    2. x: i32,
    3. y: i32,
    4. }
    5. struct PointRef<'a> {
    6. x: &'a mut i32,
    7. y: &'a mut i32,
    8. }
    9. fn main() {
    10. let mut point = Point { x: 0, y: 0 };
    11. {
    12. let r = PointRef { x: &mut point.x, y: &mut point.y };
    13. *r.x = 5;
    14. *r.y = 6;
    15. }
    16. assert_eq!(5, point.x);
    17. assert_eq!(6, point.y);
    18. }

    更新语法(Update syntax)

    一个包含..struct表明你想要使用一些其它结构体的拷贝的一些值。例如:

    1. struct Point3d {
    2. x: i32,
    3. y: i32,
    4. z: i32,
    5. }
    6. let mut point = Point3d { x: 0, y: 0, z: 0 };
    7. point = Point3d { y: 1, .. point };

    这给了point一个新的y,不过保留了xz的值。这也并不必要是同样的struct,你可以在创建新结构体时使用这个语法,并会拷贝你未指定的值:

    1. # struct Point3d {
    2. # x: i32,
    3. # y: i32,
    4. # z: i32,
    5. # }
    6. let origin = Point3d { x: 0, y: 0, z: 0 };
    7. let point = Point3d { z: 1, x: 2, .. origin };

    元组结构体

    Rust 有像另一个元组和结构体的混合体的数据类型。元组结构体有一个名字,不过它的字段没有。他们用struct关键字声明,并元组前面带有一个名字:

    1. struct Color(i32, i32, i32);
    2. struct Point(i32, i32, i32);
    3. let black = Color(0, 0, 0);
    4. let origin = Point(0, 0, 0);

    这里blackorigin并不是相同的类型,即使它们有一模一样的值。

    元组结构体结构体的成员可以使用点标记或者解构let访问,就像常规的元组:

    1. # struct Color(i32, i32, i32);
    2. # struct Point(i32, i32, i32);
    3. # let black = Color(0, 0, 0);
    4. # let origin = Point(0, 0, 0);
    5. let black_r = black.0;
    6. let Point(_, origin_y, origin_z) = origin;

    Point(_, origin_y, origin_z)这样的模式也可以用于match 表达式。

    一个元组结构体非常有用的情况是当他只有一个元素时,我们称之为“新类型(newtype)”模式,因为它允许创建一个区别于它包含的值的类型,同时也标明它的语义:

    1. struct Inches(i32);
    2. let length = Inches(10);
    3. let Inches(integer_length) = length;
    4. println!("length is {} inches", integer_length);

    如上所示,通过解构let可以获取其中的整型值。在这里,let Inches(integer_length)10赋值于integer_length。我们可以用点标记做到同样的事:

    1. # struct Inches(i32);
    2. # let length = Inches(10);
    3. let integer_length = length.0;

    几乎总是可以在使用元组结构体的地方使用struct,并可能更明确一些。我们可以这样重写ColorPoint

    1. struct Color {
    2. red: i32,
    3. blue: i32,
    4. green: i32,
    5. }
    6. struct Point {
    7. x: i32,
    8. y: i32,
    9. z: i32,
    10. }

    好的名字是很重要的,同时元组结构体中的值也可以使用点语法被引用,struct提供了真实的名字,而不仅仅是位置。

    类单元结构体(Unit-like structs)

    你可以定义一个没有任何成员的结构体:

    1. struct Electron {} // Use empty braces...
    2. struct Proton; // ...or just a semicolon.
    3. // Use the same notation when creating an instance.
    4. let x = Electron {};
    5. let y = Proton;
    6. let z = Electron; // Error

    这样的结构体叫做“类单元”因为它与一个空元组类似,(),这有时叫做“单元”。就像一个元组结构体,它定义了一个新类型。

    就它本身来看没什么用(虽然有时它可以作为一个标记类型),不过在与其它功能的结合中,它可以变得有用。例如,一个库可能请求你创建一个实现了一个特定特性的结构来处理事件。如果你并不需要在结构中存储任何数据,你可以仅仅创建一个类单元结构体。