Rust中是否有传统风格的switch语句?

16

我想将一个值与常量或其他值进行比较。在另一种语言(例如JavaScript)中,我会这样做:

// Pretend these are values that are generated another way
let a = 23;
let b = 16;
let c = 43;
let input = 23;

switch (input) {
    case a: console.log("Input is equal to a"); break;
    case b: console.log("Input is equal to b"); break;
    case c: console.log("Input is equal to c"); break;
    default: console.log("Input does not equal any value"); break;
}

我如何在Rust中优雅地做到这一点?我知道可以使用if语句,但我认为这很混乱,而且我要比较更多的值。

我能否使用匹配语句在Rust中将变量与常量值进行比较?


1
只是为了澄清, abc是常数还是在运行时生成的? - SCappella
@用户 是的,它们是。 - flmng0
1
如果它们是在运行时生成的,在Rust中它们就不是常量。那么它是哪一个? - SCappella
@User 对不起,那不是我想说的。我读错了你的问题,以为你问的是它们是否是常量。它们是常量。 - flmng0
2
我建议你阅读这本书来学习Rust。看起来你还没有阅读过它。特别是要查看第6章以获取答案。 - hellow
2个回答

27

如果您事先知道案例的值,并且不介意将它们放在代码中间,则执行此类案例分析的最简单方法是使用简单的match表达式。

fn main() {
    for &input in &[16, 23, 42, 43] {
        match input {
            23 => println!("Input is equal to a"),
            16 => println!("Input is equal to b"),
            43 => println!("Input is equal to c"),
            _ => println!("Input does not equal any value"),
        }
    }
}

(Playground链接)

如果您的abc是编译时常量(即已知值或使用const函数生成的值),则仍然可以直接进行匹配。

const A: i32 = 23;
const B: i32 = 16;
const C: i32 = generate_c();

const fn generate_c() -> i32 {
    A + B + 4
}

fn main() {
    for &input in &[16, 23, 42, 43] {
        match input {
            A => println!("Input is equal to a"),
            B => println!("Input is equal to b"),
            C => println!("Input is equal to c"),
            _ => println!("Input does not equal any value"),
        }
    }
}

(示例链接)

然而,如果您尝试对非常量变量进行此操作,则会得到奇怪的输出。

fn generate_c(a: i32, b: i32) -> i32 {
    a + b + 4
}

fn main() {
    let a = 23;
    let b = 16;
    let c = generate_c(a, b);

    for &input in &[16, 23, 42, 43] {
        match input {
            a => println!("Input is equal to a"),
            b => println!("Input is equal to b"),
            c => println!("Input is equal to c"),
            _ => println!("Input does not equal any value"),
        }
    }
}

(playground链接)

如果您运行这个程序,编译器将会给您很多"不可到达的模式"的警告,并且输出结果将会是四次"输入等于a"。问题在于,match语句中每行左侧并不是简单的表达式,而是一个模式

模式是如(x, [_, z], Some(_))这样的一种表达式。它由基础变量(比如xz)、下划线符号(_)、所有字面表达式(整数、浮点数、元组、数组)以及其他几种内容构成。

当Rust运行match语句时,它会尝试将输入与模式进行句法匹配。基础变量可以匹配任何东西,该变量的值会被设置为该分支的作用域内所匹配到的内容。下划线符号(就像上面所有示例中使用的那样)也可以匹配任何东西,但它们并不绑定任何变量。

使用上述的const版本时,常量ABC会在代码中的各处替换为它们各自的字面值,所以输入会与这些字面值进行匹配。

使用变量版本时,在我们匹配abc时,这些字母被解释为基础变量,可以匹配任何东西。模式中不考虑变量的实际值。在代码中

let a = 14;
let b = 15;
let c = 16;
let input = 16;
match input {
    a => println!("Input is equal to a"),
    b => println!("Input is equal to b"),
    c => println!("Input is equal to c"),
    _ => println!("Input does not equal any value"),
}

第一个分支将总是匹配,并为其作用域中的输入赋予名称a

如果您需要匹配变量a,bc,则可以向每个分支添加一个保护条件。保护条件通过为分支添加附加条件来对分支进行进一步过滤以实现匹配。在本例中,我们匹配任何内容并将其绑定到变量x,但接着检查x是否等于a(以及bc)。

fn generate_c(a: i32, b: i32) -> i32 {
    a + b + 4
}

fn main() {
    let a = 23;
    let b = 16;
    let c = generate_c(a, b);

    for &input in &[16, 23, 42, 43] {
        match input {
            x if x == a => println!("Input is equal to a"),
            x if x == b => println!("Input is equal to b"),
            x if x == c => println!("Input is equal to c"),
            _ => println!("Input does not equal any value"),
        }
    }
}

(playground链接)

这种写法比switch / case结构稍微冗长一些,但我希望你能清楚地理解正在发生的事情。在每个分支中,变量x被绑定到16,然后如果它等于变量A(或BC),则执行该分支。否则,我们尝试下一个分支。


2
你还可以提到,你可以在模式中使用字面量,例如 match input { 8 => {}, 16 => {}, 30 => {} } - hellow
如果你确定其他情况不可能被触发,你可以使用 _ => unreachable!() - Nick McCurdy

2
我能使用match语句将变量与常量值在Rust中进行比较吗?我写了一个来(几乎)实现这个功能。它会扩展为带有if条件的match语句。
switch! { input;
    a => println!("Input is equal to a"),
    b => println!("Input is equal to b"),
    c => println!("Input is equal to c"),
    _ => println!("Input does not equal any value"),
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接