你如何避免在Rust中的代码块内意外重新声明变量?

时间:2016-12-09 01:12:51

标签: rust

我正在学习Rust,我遇到了这个:

let mut x: i32 = 1;
x = 7;
let x = x; // x is now immutable and is bound to 7

let y = 4;
let y = "I can also be bound to text!"; // y is now of a different type

这怎么可能安全?请查看以下内容:

let temp = 23;

// 200 lines of code

// I'm changing something here <====== IMPORTANT
let temp = 101;

// 200 lines of code

// do something with temp (with 23 not 101 !!!)

如果我再次let temp = 101,我无法知道我刚刚在代码中创建了一个令人讨厌的错误。在输入let temp之前,我是否真的需要搜索let temp?我错过了什么?

4 个答案:

答案 0 :(得分:7)

问题不是Rust,或者任何编程语言,它都是代码。特别是这个&#34;行&#34;:

// 200 lines of code

如果你在变量声明和使用它之间编写200(或400!)行代码,很可能会发生意外情况。但是,如果变量是可变的并且您意外地更改了它,那么也可能发生这种情况:

let mut a = 1;
a += 1;
// or
let a = 1;
let a = 2;

两者都有相同的最终结果,但其中只有一个涉及阴影。

就个人而言,我发现使用Rust的包装类型(如OptionResult)时,阴影非常有用:

fn foo(name: Option<&str>) {
    let name = name.unwrap_or("Who are you");
    println!("{}", name);
}

如果您发现自己无法处理变量阴影,那么我建议您查看Clippy,其中有多个shadow_* lint,您可以打开并禁止您的代码来自编译,如果你有任何阴影。

  

有时候你正在改变别人的代码。我无法知道a已被使用。我如何选择一个变量名呢?

我建议您在修改代码之前阅读代码。有些编辑还允许在文本中搜索模式或突出显示模式。

  

我来自Java,那是完全倒退的!这意味着,一旦您在范围内声明int x,您就无法重新声明它。

这是真的,但您仍然可以隐藏您的实例或类变量:

public class HelloWorld {
  static int a = 42;

  public static void main(String[] args) {
    int a = 21;
    System.out.print(a); // Oh no, it's not 42 anymore!
  }
}

Java编译器不能防止这个错误;它取决于您,您的测试,代码审查或外部linting工具。

  

如何避免在Rust中的代码块内意外重新声明变量?

你桌子旁边可能有一张小纸条。在上面写上数字零。每次声明一个新变量时,都会在数字中添加一个变量。使用该数字为所有变量添加前缀或后缀。

为了安全起见,您还可以将您的名字添加到变量中 - 您不希望与遵循相同算法的任何其他人发生冲突。您可能还希望将函数和类型添加到变量的名称中,以避免不同函数之间的冲突。

当然,这些变量名称可能很长。要解决这个问题,您可能希望缩写上述所有内容,并将数字写为十六进制或Base64。您可以使用相当独特的my_type_cool_function_john_doe_x_12223123,而不是mtcfjdxBA8293。这不太可能与任何现有变量发生冲突。

(以上是讽刺)

毫无疑问:我强烈认为问题是变量阴影:它的数百行长的函数需要如此多的精神开销。在这些类型上创建函数,创建类型和方法,但以某种方式修复代码,然后再将其进一步更改。

答案 1 :(得分:3)

任何具有覆盖100行代码的生命周期的变量都应该具有长且具有描述性的名称,并且通常不应该被遮挡。

tempx之类的名称只能用于生命周期的变量 - 十几行或更少 - 以便对变量的任何使用,你可以在几行之前轻松看到它的定义。

答案 2 :(得分:3)

另一点,只是针对Java比较以及正在使用的“删除”或“覆盖”散文,例如:

  

每次让x = 3时吓人都没有意义;因为你可能正在删除先前声明的值。

Rust仍然有范围规则。阴影尊重范围。与Java不同,您可以隐藏变量,但它会保留原始范围。 For example

fn main() {
    let x = 3;

    {
        let x = 5;

        println!("Inner scope: {}", x); // Prints 5
    }

    println!("Outer scope: {}", x); // Prints 3
}

阴影考虑了范围。

答案 3 :(得分:2)

如果你有一个长达数百行的方法并且想要引入一个新变量,我建议你不要在long方法中添加更多变量,而是在你想要的地方调用一个函数。引入一个新变量。然后,您将获得两个优势:一个新的范围,您知道您的变量不是阴影或阴影,而且您不会在已经很长的方法中添加代码。