枚举

enum是enumerations的简称。它们看起来与结构体非常相似,但又有所不同。这就是区别:

  • 当你想要一个东西另一个东西时,使用struct.
  • 当你想要一个东西另一个东西时,请使用 enum

所以,结构体是用于多个事物在一起,而枚举则是用于多个选择在一起。

要声明一个枚举,请写enum,并使用一个包含选项的代码块,用逗号分隔。就像 struct 一样,最后一部分可以有逗号,也可以没有。我们将创建一个名为 ThingsInTheSky 的枚举。

enum ThingsInTheSky {
    Sun,
    Stars,
}

fn main() {}

这是一个枚举,因为你可以看到太阳,星星:你必须选择一个。这些叫做变体

// create the enum with two choices
enum ThingsInTheSky {
    Sun,
    Stars,
}

// With this function we can use an i32 to create ThingsInTheSky.
fn create_skystate(time: i32) -> ThingsInTheSky {
    match time {
        6..=18 => ThingsInTheSky::Sun, // Between 6 and 18 hours we can see the sun
        _ => ThingsInTheSky::Stars, // Otherwise, we can see stars
    }
}

// With this function we can match against the two choices in ThingsInTheSky.
fn check_skystate(state: &ThingsInTheSky) {
    match state {
        ThingsInTheSky::Sun => println!("I can see the sun!"),
        ThingsInTheSky::Stars => println!("I can see the stars!")
    }
}

fn main() {
    let time = 8; // it's 8 o'clock
    let skystate = create_skystate(time); // create_skystate returns a ThingsInTheSky
    check_skystate(&skystate); // Give it a reference so it can read the variable skystate
}

这将打印出I can see the sun!

你也可以将数据添加到一个枚举中。

enum ThingsInTheSky {
    Sun(String), // Now each variant has a string
    Stars(String),
}

fn create_skystate(time: i32) -> ThingsInTheSky {
    match time {
        6..=18 => ThingsInTheSky::Sun(String::from("I can see the sun!")), // Write the strings here
        _ => ThingsInTheSky::Stars(String::from("I can see the stars!")),
    }
}

fn check_skystate(state: &ThingsInTheSky) {
    match state {
        ThingsInTheSky::Sun(description) => println!("{}", description), // Give the string the name description so we can use it
        ThingsInTheSky::Stars(n) => println!("{}", n), // Or you can name it n. Or anything else - it doesn't matter
    }
}

fn main() {
    let time = 8; // it's 8 o'clock
    let skystate = create_skystate(time); // create_skystate returns a ThingsInTheSky
    check_skystate(&skystate); // Give it a reference so it can read the variable skystate
}

这样打印出来的结果是一样的:I can see the sun!

你也可以 "导入"一个枚举,这样你就不用打那么多字了。下面是一个例子,我们每次在心情上匹配时都要输入 Mood::

enum Mood {
    Happy,
    Sleepy,
    NotBad,
    Angry,
}

fn match_mood(mood: &Mood) -> i32 {
    let happiness_level = match mood {
        Mood::Happy => 10, // Here we type Mood:: every time
        Mood::Sleepy => 6,
        Mood::NotBad => 7,
        Mood::Angry => 2,
    };
    happiness_level
}

fn main() {
    let my_mood = Mood::NotBad;
    let happiness_level = match_mood(&my_mood);
    println!("Out of 1 to 10, my happiness is {}", happiness_level);
}

它打印的是Out of 1 to 10, my happiness is 7。让我们导入,这样我们就可以少打点字了。要导入所有的东西,写*。注意:它和*的解引用键是一样的,但完全不同。

enum Mood {
    Happy,
    Sleepy,
    NotBad,
    Angry,
}

fn match_mood(mood: &Mood) -> i32 {
    use Mood::*; // We imported everything in Mood. Now we can just write Happy, Sleepy, etc.
    let happiness_level = match mood {
        Happy => 10, // We don't have to write Mood:: anymore
        Sleepy => 6,
        NotBad => 7,
        Angry => 2,
    };
    happiness_level
}

fn main() {
    let my_mood = Mood::Happy;
    let happiness_level = match_mood(&my_mood);
    println!("Out of 1 to 10, my happiness is {}", happiness_level);
}

enum 的部分也可以变成一个整数。这是因为 Rust 给 enum 的每个arm提供了一个以 0 开头的数字,供它自己使用。如果你的枚举中没有任何其他数据,你可以用它来做一些事情。

enum Season {
    Spring, // If this was Spring(String) or something it wouldn't work
    Summer,
    Autumn,
    Winter,
}

fn main() {
    use Season::*;
    let four_seasons = vec![Spring, Summer, Autumn, Winter];
    for season in four_seasons {
        println!("{}", season as u32);
    }
}

这个打印:

0
1
2
3

不过如果你想的话,你可以给它一个不同的数字--Rust并不在意,可以用同样的方式来使用它。只需在你想要的变体上加一个 = 和你的数字。你不必给所有的都分配一个数字。但如果你不这样做,Rust就会从前一个arm加1来赋值给当前arm。

enum Star {
    BrownDwarf = 10,
    RedDwarf = 50,
    YellowStar = 100,
    RedGiant = 1000,
    DeadStar, // Think about this one. What number will it have?
}

fn main() {
    use Star::*;
    let starvec = vec![BrownDwarf, RedDwarf, YellowStar, RedGiant];
    for star in starvec {
        match star as u32 {
            size if size <= 80 => println!("Not the biggest star."), // Remember: size doesn't mean anything. It's just a name we chose so we can print it
            size if size >= 80 => println!("This is a good-sized star."),
            _ => println!("That star is pretty big!"),
        }
    }
    println!("What about DeadStar? It's the number {}.", DeadStar as u32);
}

这个打印:

Not the biggest star.
Not the biggest star.
This is a good-sized star.
This is a good-sized star.
What about DeadStar? It's the number 1001.

DeadStar本来是4号,但现在是1001。

使用多种类型的枚举

你知道Vec、数组等中的元素都需要相同的类型(只有tuple不同)。但其实你可以用一个枚举来放不同的类型。想象一下,我们想有一个Vec,有u32i32。当然,你可以创建一个Vec<(u32, i32)>(一个带有(u32, i32)元组的vec),但是我们每次只想要一个。所以这里可以使用一个枚举。下面是一个简单的例子。

enum Number {
    U32(u32),
    I32(i32),
}

fn main() {}

所以有两个变体:U32变体里面有u32I32变体里面有i32U32I32只是我们起的名字。它们可能是UThirtyTwoIThirtyTwo或其他任何东西。

现在,如果我们把它们放到 Vec 中,我们就会有一个 Vec<Number>,编译器很高兴,因为都是同一个类型。编译器并不在乎我们有 u32i32,因为它们都在一个叫做 Number 的单一类型里面。因为它是一个枚举,你必须选择一个,这就是我们想要的。我们将使用.is_positive()方法来挑选。如果是 true,那么我们将选择 U32,如果是 false,那么我们将选择 I32

现在的代码是这样的。

enum Number {
    U32(u32),
    I32(i32),
}

fn get_number(input: i32) -> Number {
    let number = match input.is_positive() {
        true => Number::U32(input as u32), // change it to u32 if it's positive
        false => Number::I32(input), // otherwise just give the number because it's already i32
    };
    number
}


fn main() {
    let my_vec = vec![get_number(-800), get_number(8)];

    for item in my_vec {
        match item {
            Number::U32(number) => println!("It's a u32 with the value {}", number),
            Number::I32(number) => println!("It's an i32 with the value {}", number),
        }
    }
}

这就打印出了我们想看到的东西。

It's an i32 with the value -800
It's a u32 with the value 8