循环和while true有什么区别?

41
Rust教程和book现在声称while trueloop之间存在差异,但在这个阶段理解它并不是非常重要。

If you need an infinite loop, you may be tempted to write this:

while true {

However, Rust has a dedicated keyword, loop, to handle this case:

loop {

Rust's control-flow analysis treats this construct differently than a while true, since we know that it will always loop. The details of what that means aren't super important to understand at this stage, but in general, the more information we can give to the compiler, the better it can do with safety and code generation, so you should always prefer loop when you plan to loop infinitely.

我曾经做过一些编译器相关的工作,我不禁想知道它们之间可能存在的语义差异,因为编译器可以轻松地确定两者都是无限循环。

那么,编译器如何对待它们?

5个回答

44
这是在Reddit上回答的。就像你说的那样,编译器可以特别处理while true,但它没有这样做。由于它没有这样做,编译器不能从语义上推断出在while true循环中设置的未声明变量必须始终初始化(如果你退出循环),但对于loop循环却可以:

It also helps the compiler reason about the loops, for example

let x;
loop { x = 1; break; }
println!("{}", x)

is perfectly valid, while

let x;
while true { x = 1; break; }
println!("{}", x);

fails to compile with "use of possibly uninitialised variable" pointing to the x in the println. In the second case, the compiler is not detecting that the body of the loop will always run at least once.

(Of course, we could special case the construct while true to act like loop does now. I believe this is what Java does.)


5
谢谢,虽然这也引出了一个问题:为什么? - aij
1
@aij 几乎可以确定是因为 while true 暗示你可以将 true 提取到一个变量中或从函数中返回它,这会导致循环终止。这不是一个好的开发者体验。 - Richiban

30

一个主要的区别是,loop 可以通过将值传递给 break返回一个值。而 whilefor 则不行

fn main() {
    let mut counter = 0;

    let result = loop {
        counter += 1;

        if counter == 10 {
            break counter * 2;
        }
    };

    assert_eq!(result, 20);
}

5
首先要说的是,就性能而言,它们很可能是相同的。虽然 Rust 本身对 while true 没有特殊处理,但 LLVM 可能会进行优化。Rust 编译器试图通过将优化委托给 LLVM 来保持简单。

一般来说,我们向编译器提供的信息越多,它在安全性和代码生成方面的表现就越好。

尽管某些常量表达式可能会被 LLVM 优化掉,但语言的语义并不因表达式是否为常量而改变。这很好,因为它也帮助人类更好地理解代码。
仅仅因为 true 是一个简单的表达式,我们知道它是常量。所以 true != false[0; 1].len() == 1 也是常量。但是 num_cpus::get() == 1 呢?我其实不知道是否存在某些编译目标可以将其视为常量,但我也不应该去考虑! telotortium's example中的错误,当与生成的代码或宏组合时会更加显著。想象一下一个宏,有时会产生一个简单的静态表达式,比如true == true,但有时会引用变量或调用函数。有时编译器能够确定循环运行一次,但有时却不能。在Rust中,现在该示例中的错误将始终是一个错误,无论为该条件生成了什么代码。没有任何意外。

0
循环和while true有什么区别?
你可以问一下for和while的区别吗?答案会接近于:什么是编程习惯? 当你写while condition {}时,你是在说“只要条件为真,就执行”,但我们可以看到,“只要true为真,就执行”是多余的。这就是loop的用处,它可以很好地表达无限循环,因为我们说“loop on that”。我们没有任何条件,这更好。
那么,编译器如何对待它们的不同之处呢?

我无法回答“如何”这个问题,但我想你想知道的是“为什么”。它允许编译器知道这个循环至少会运行一次,就像 C 语言中的 do {} while (condition);。编译器可以利用这些信息来生成更好的代码或警告。此外,您将确信循环将被执行,而 while 循环可能会因为编译器优化而消失。有趣的是,Rust 在内部使用 LLVM,而 LLVM 看起来没有一种表达无限循环的方式,因此在某些情况下会产生 bugs


0

简而言之,loop 是一个表达式;while true 是一个语句,它们有不同的用例并提供灵活性和表现力。

主要原因是 loop 块是表达式,而 while 块是语句。区分两者并为每种情况设置不同的规则,使编译器能够进行优化而不牺牲安全性。

虽然 loopwhile true 循环具有相同的行为(可能有类似的优化——每个 while true 循环迭代开始时的条件检查被删除以提高性能),但它们仍然受到 Rust 实现的限制,不能互换使用。

例如,我们可以像这样使用 loop 循环:

let mut counter = 0;
let result = loop {
    counter += 1;
    if counter == 10 {
        break counter * 2; // Return a value from the loop
    }
};
println!("The result is {}", result); // The result is 20

我们可以看到,我们将loop表达式的评估结果分配给结果而没有问题。
另一方面,while循环是语句,因此我们无法将它们分配给变量等。
使用while true循环执行相同操作的代码如下:
let mut counter = 0;
let mut result = 0;
while counter < 10 {
    counter += 1;
    if counter == 10 {
        result = counter * 2; // Assign a value inside the loop
        break;
    }
};
println!("The result is {}", result); // The result is 20

如果我们尝试使用while true来实现这个简单的算法,就像loop一样,会产生以下编译器错误:
let mut counter = 0;
let mut result = while counter < 10 {
    counter += 1;
    if counter == 10 {
        counter * 2; // Statement, hence () is returned
        break
    }
};
println!("The result is {}", result); // `()` cannot be formatted with the default formatter

error[E0277]: `()` doesn't implement `std::fmt::Display`
  --> src/main.rs:10:34
   |
10 |     println!("The result is {}", result); // The result is 20
   |                                  ^^^^^^ `()` cannot be formatted with the default formatter
   |
   = help: the trait `std::fmt::Display` is not implemented for `()`
   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)

1
这不正确。在Rust中,两个循环都是表达式,只是while始终返回()。已经有一个回答涵盖了您可以从loop中带值中断但不能从while中断的事实。 - Chayim Friedman

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