如何在不赋值的情况下注释 Rust 变量的数据类型?

3
我是一位有用的助手,可以翻译文字。

我刚接触Rust,想声明一个未初始化的有类型变量,这在Rust中是否可行?如果可以,是否建议这样做呢?或者有更加符合Rust风格的方式吗?

我正在查看来自rustlings的以下代码,由于无法推断x的类型而无法编译:

    let x;
    if x == 10 {
        println!("Ten!");
    }

所以,我尝试了这个:

let x: i32;

然而,现在编译器会在比较行上抛出错误,表示x未被声明。
这种方式是可行的,但我一直在思考能否在没有赋值的情况下声明类型。
let x: i32 = 10;

或者说正确的方法是设计你的代码以避免在第一时间出现这种情况吗?我来自C语言背景,可能对类型懒惰得有些过分。


你可以声明一个变量(及其类型)而不进行初始化,但 Rust 编译器会(正确地)防止在变量未被初始化前读取其值。请参见https://doc.rust-lang.org/nomicon/checked-uninit.html。 - Alistair A. Israel
此外,自从C/C++时代以来普遍使用的“先声明后使用”的模式在现代语言中已不再受欢迎。相反,最佳实践是在最接近使用变量的地方声明变量。请参见https://doc.rust-lang.org/rust-by-example/variable_bindings/declare.html。 - Alistair A. Israel
从技术上讲,您可以执行类似于 let x = unsafe { mem::uninitialized::<i32>() } 的操作。 - Bubletan
2个回答

3
在 Rust 中,变量的类型通常是从上下文中推断出来的。
上下文可以包括以下任何内容:
  • 被赋值的值的类型:
let x = String::new(); //x is now a String
  • 被调用函数的返回类型:
fn foo() -> usize {
    40
}
let x = foo(); //x is now usize

但是如果你没有类型可以推断,比如变量没有赋值:

let x;

那么,Rust 就无法推断类型。因此,它会对你生气

这个答案还需要解决另一个问题:Rust 不允许读取未初始化的变量。下面的值 x 是未初始化的:

let x: u32;

读取未初始化的u32变量的值可能不会有太大影响,但考虑以下情况:

struct MyString {
    ptr: *const u8,
    len: usize,
    cap: usize,
}

如果我们能为这个提供一个安全的包装器,这将是完全可以的,因为这本质上就是 标准库String 的精髓所在。但是,如果我们拥有一个未初始化的 MyString 并尝试读取它的内容,我们将从 ptr 读取一个垃圾地址,以及垃圾的 lencap。这将导致读取一个垃圾指针,这是未定义的行为,这是 Rust 着重消除的第一优先级。
所以,简要概括一下:
✓ 每个变量必须具有在运行时确定的类型(包括已经擦除的类型,因为那些也是具体的类型 [dyn Traitimpl Trait],但是 impl Trait 是特殊的)
✓ 每个变量在您读取其值之前必须被分配某种值。
✓ Rust 非常努力地消除所有未定义的行为。

0

只要编译器能够看到在尝试读取之前它总是有一个值被分配,并且它只会被分配一次,那么您就可以这样做。这在特殊情况下可能会有所帮助。这里有一个稍微牵强的例子

fn main() {
    let some_cond = false;

    let x;
    let y;
    let z: u8;

    if some_cond {
        x = 1;
        y = 2;
        z = 3;
    } else {
        x = 4;
        y = 5;
        z = 6;
    }

    println!("x = {}", x); // "x = 4"
    println!("y = {}", y); // "y = 5"
    println!("z - 10 = {}", z.wrapping_sub(10)); // "z - 10 = 252"
}

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