您的当前位置:首页正文

看例学Rust[三]---自定义类型

来源:要发发知识网

在 Rust 中自定义的数据类型主要是通过下面两个关键字:

  • struct: 定义结构体
  • enum: 定义枚举类型

常量是用conststatic这两个关键字来声明.

1. 结构体

struct关键字能够定义三种不同的结构体:

  • 最基本的元组结构体,又叫元组(Tupels)
  • 经典的类C结构体
  • 单元结构体(泛结构体?)没有成员

示例

// A unit struct
// 单元结构体(泛结构体?)
struct Nil;

// A tuple struct
// 元组结构体
struct Pair(i32, f64);

// A struct with two fields
// 拥有两个成员的结构体
struct Point {
    x: f64,
    y: f64,
}

// Structs can be reused as fields of another struct
// 结构体能够作为其它结构体的成员
#[allow(dead_code)]
struct Rectangle {
    p1: Point,
    p2: Point,
}

fn main() {
    // Instantiate a `Point`
    // 实例化一个点
    let point: Point = Point { x: 0.3, y: 0.4 };

    // Access the fields of the point
    // 访问改点的成员
    println!("point coordinates: ({}, {})", point.x, point.y);

    // Destructure the point using a `let` binding
    // 用 let 分解,同时得到各成员值
    let Point { x: my_x, y: my_y } = point;

    let _rectangle = Rectangle {
        // struct instantiation is an expression too
        // 结构体的实例化是表达式,即有返回值
        p1: Point { x: my_y, y: my_x },
        p2: point,
    };

    // Instantiate a unit struct
    // 实例化一个单元结构体
    let _nil = Nil;

    // Instantiate a tuple struct
    // 实例化一个元组结构体
    let pair = Pair(1, 0.1);

    // Destructure a tuple struct
    // 结构一个元组结构体
    let Pair(integer, decimal) = pair;

    println!("pair contains {:?} and {:?}", integer, decimal);
}

结果

point coordinates: (0.3, 0.4)
pair contains 1 and 0.1

2.枚举

enum关键字能够创建包含不同数据类型的类型.对struct有效的类型同样对enum有效.

示例

// An attribute to hide warnings for unused code.
// 对未使用的代码忽略警告
#![allow(dead_code)]

// Create an `enum` to classify someone. Note how both names
// and type information together specify the variant:
// 用` enum `创建一个"人"的类, 注意,名字和类型信息都是指特定的数据类型:
// `Skinny != Fat` and `Height(i32) != Weight(i32)`. Each
// is different and independent.
//  `Skinny != Fat` and `Height(i32) != Weight(i32)`. 他们是不同的,独立的.
enum Person {
    // An `enum` may either be `unit-like`,
    // 一个枚举可以是 单元
    Skinny,
    Fat,
    // like tuple structs,
    // 元组
    Height(i32),
    Weight(i32),
    // or like structures.
    // 结构体
    Info { name: String, height: i32 }
}

// A function which takes a `Person` enum as an argument and
// returns nothing.
// 一个需要传入 `Person`枚举作为参数,无返回值的函数.
fn inspect(p: Person) {
    // Usage of an `enum` must cover all cases (irrefutable)
    // so a `match` is used to branch over it.
    // 枚举的使用必须覆盖所有条目,所以` match `里面有它所有的分支.
    match p {
        Person::Skinny    => println!("Is skinny!"),
        Person::Fat       => println!("Is fat!"),
        // Destructure `i` from inside the `enum`.
        // 从枚举中分解出 ` i `
        Person::Height(i) => println!("Has a height of {}.", i),
        Person::Weight(i) => println!("Has a weight of {}.", i),
        // Destructure `Info` into `name` and `height`.
        // 分解出` Info ` 到 `name` and `height`.
        Person::Info { name, height } => {
            println!("{} is {} tall!", name, height);
        },
    }
}

fn main() {
    let person = Person::Height(18);
    let danny  = Person::Weight(10);
    // `to_owned()` creates an owned `String` from a string slice.
    // `to_owned()`创建字符串切片的`String`类型.
    let dave   = Person::Info { name: "Dave".to_owned(), height: 72 };
    let john   = Person::Fat;
    let larry  = Person::Skinny;

    inspect(person);
    inspect(danny);
    inspect(dave);
    inspect(john);
    inspect(larry);
}

结果

Has a height of 18.
Has a weight of 10.
Dave is 72 tall!
Is fat!
Is skinny!

2.1 use

use关键字来代替获取.

// An attribute to hide warnings for unused code.
// 对未使用的代码忽略警告
#![allow(dead_code)]

enum Status {
    Rich,
    Poor,
}

enum Work {
    Civilian,
    Soldier,
}

fn main() {
    // Explicitly `use` each name so they are available without
    // manual scoping.
    // ` use `带上每一个名字
    use Status::{Poor, Rich};
    // Automatically `use` each name inside `Work`.
    // 自动` use `所有名字
    use Work::*;

    // Equivalent to `Status::Poor`.
    // 等价于`Status::Poor`
    let status = Poor;
    // Equivalent to `Work::Civilian`.
    // 等价于`Work::Civilian`
    let work = Civilian;

    match status {
        // Note the lack of scoping because of the explicit `use` above.
        // 注意不需要带上域(`Status`),因为明确地使用了`use`
        Rich => println!("The rich have lots of money!"),
        Poor => println!("The poor have no money..."),
    }

    match work {
        // Note again the lack of scoping.
        // 同样不用带上域(`Work`)
        Civilian => println!("Civilians work!"),
        Soldier  => println!("Soldiers fight!"),
    }
}

结果

The poor have no money...
Civilians work!

2.2. 类C枚举

enum 同样可以作为C中的枚举类型使用.

// An attribute to hide warnings for unused code.
// 对未使用的代码忽略警告
#![allow(dead_code)]

// enum with implicit discriminator (starts at 0)
// 
enum Number {
    Zero,
    One,
    Two,
}

// enum with explicit discriminator
enum Color {
    Red = 0xff0000,
    Green = 0x00ff00,
    Blue = 0x0000ff,
}

fn main() {
    // `enums` can be cast as integers.
    println!("zero is {}", Number::Zero as i32);
    println!("one is {}", Number::One as i32);

    println!("roses are #{:06x}", Color::Red as i32);
    println!("violets are #{:06x}", Color::Blue as i32);
}

结果

zero is 0
one is 1
roses are #ff0000
violets are #0000ff

2.3 测试用例:链表

枚举比较常见用来创建链表.

示例

use List::*;

enum List {
    // Cons: Tuple struct that wraps an element and a pointer to the next node
    Cons(u32, Box<List>),
    // Nil: A node that signifies the end of the linked list
    Nil,
}

// Methods can be attached to an enum
impl List {
    // Create an empty list
    fn new() -> List {
        // `Nil` has type `List`
        Nil
    }

    // Consume a list, and return the same list with a new element at its front
    fn prepend(self, elem: u32) -> List {
        // `Cons` also has type List
        Cons(elem, Box::new(self))
    }

    // Return the length of the list
    fn len(&self) -> u32 {
        // `self` has to be matched, because the behavior of this method
        // depends on the variant of `self`
        // `self` has type `&List`, and `*self` has type `List`, matching on a
        // concrete type `T` is preferred over a match on a reference `&T`
        match *self {
            // Can't take ownership of the tail, because `self` is borrowed;
            // instead take a reference to the tail
            Cons(_, ref tail) => 1 + tail.len(),
            // Base Case: An empty list has zero length
            Nil => 0
        }
    }

    // Return representation of the list as a (heap allocated) string
    fn stringify(&self) -> String {
        match *self {
            Cons(head, ref tail) => {
                // `format!` is similar to `print!`, but returns a heap
                // allocated string instead of printing to the console
                format!("{}, {}", head, tail.stringify())
            },
            Nil => {
                format!("Nil")
            },
        }
    }
}

fn main() {
    // Create an empty linked list
    let mut list = List::new();

    // Append some elements
    list = list.prepend(1);
    list = list.prepend(2);
    list = list.prepend(3);

    // Show the final state of the list
    println!("linked list has length: {}", list.len());
    println!("{}", list.stringify());
}

结果

linked list has length: 3
3, 2, 1, Nil