不使用unsafe或者panic的情况下,是否可以将一个可能未初始化的变量标记为可用的?

4

在不使用unsafe或可能调用panic!的代码的情况下,是否有可能向编译器解释变量v在标记为1的行中表现良好?

#[derive(PartialEq, Debug)]
enum Enum {
    V1,
    V2,
    V3,
}

fn main() {
    let e = Enum::V1;

    let mut v: i32;

    if e == Enum::V1 || e == Enum::V2 {
        v = 17; //some complex, costy expression
    }
    match e {
        Enum::V1 | Enum::V2 => {
            println!("Results: {}", v); //1
        }
        _ => {}
    }
}

编译器报告如下:
error[E0381]: use of possibly uninitialized variable: `v`
  --> src/main.rs:18:37
   |
18 |             println!("Results: {}", v); //1
   |                                     ^ use of possibly uninitialized `v`

我有一个复杂的表达式来初始化v,而不是使用17。由于v的类型没有实现Default,我只需要在Enum::V1Enum::V2情况下使用v

在真实的代码中,我分别为Enum::V1Enum::V2设置了单独的分支,并且我可以将v的初始化移到那里。

我想让我的代码更清晰,并且不想使用潜在的糟糕方法,比如unsafeOption::unwrap

2个回答

6

简单的方法是初始化v;这只是一个单词,如果不必要,编译器可能会将其优化掉。在这种特殊情况下,你甚至可以将声明和初始化都移到匹配的内部范围,因为它在其他任何地方都没有被使用。

更好的做法是使无效的情况不可表示。在这里,v只存在于V1V2的情况中,因此如果我们将两者合并,就没有可能未初始化的值的名称了。

#[derive(PartialEq, Debug)]
enum Enum {
    V1 { v: i32 },
    V2 { v: i32 },
    V3
}

fn main() {
    let mut e = Enum::V1 { v: 17 };

    match e {
        Enum::V1 {v} | Enum::V2 {v} => {
            println!("Results: {}", v);//1
        }
        _ => {}
    }
}

这就是像ResultOption这样的类型的工作原理。


2
似乎这个答案并没有真正解决潜在的问题。将值 v 放入枚举中仍然需要在某个时刻计算出 v 的值,但是并没有展示如何仅为两种必需的情况计算它。在使用其他答案之后,这个答案会更有意义。两个枚举:一个像 OP 一样,在另一个答案中使用,另一个像这个答案一样 - Shepmaster

5
在安全的Rust中,您不能这样做。在您的代码中,变量v仅在条件分支e == Enum::V1 || e == Enum::V2下被保证初始化,但是v却在更广的范围内声明。请注意,这不是一种限制,而是编译器提示程序设计应该重新考虑的提示。
在这种情况下,我建议将计算v的任务委托给一个函数,并且只在必要的情况下使用v
fn calculate_v(e: Enum) -> i32 { ... }

let e = Enum::V1;

match e {
    Enum::V1 => {
        let v = calculate_v(e);
        // use v
        println!("Results: {}", v);//1
    }
    Enum::V2 => {
        let v = calculate_v(e);
        println!("Results: {}", v);//1
    }
    _ => {}
}

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