这两个循环在C++和Rust中是等价的:

#include <cstdint>
uint64_t sum1(uint64_t n) {  
    uint64_t sum = 0;
    for (uint64_t j = 0; j <= n; ++j) {
        sum += 1;
    }
    return sum;
}
pub fn sum1(num: u64) -> u64 {
    let mut sum: u64 = 0;
    for j in 0u64..=num {
        sum += 1;
    }
    return sum;
}

但是C++版本生成了非常简洁的程序集:

sum1(unsigned long):                               # @sum1(unsigned long)
        xor     eax, eax
.LBB0_1:                                # =>This Inner Loop Header: Depth=1
        add     rax, 1
        cmp     rax, rdi
        jbe     .LBB0_1
        ret

Rust的版本很长,循环中有两个判断,而不是一个:

example::sum1:
        xor     eax, eax
        xor     ecx, ecx
.LBB0_1:
        mov     rdx, rcx
        cmp     rcx, rdi
        adc     rcx, 0
        add     rax, 1
        cmp     rdx, rdi
        jae     .LBB0_3
        cmp     rcx, rdi
        jbe     .LBB0_1
.LBB0_3:
        ret

Godbolt:https://godbolt.org/z/xYW94qxjK

什么是内在的 rust 迹,试图阻止C++无忧无虑?

推荐答案

迭代器状态下的溢出.

在给定足够大的输入时,C++版本将永远循环:

#include <cstdint>

[[gnu::noinline]]
std::uint64_t sum1(std::uint64_t n) {  
    std::uint64_t sum = 0;
    for (std::uint64_t j = 0; j <= n; ++j) {
        sum += 1;
    }
    return sum;
}

#include <iostream>

int main() {
    std::cout << sum1(UINT64_C(0xffffffff'ffffffff)) << std::endl;

    return 0;
}

这是因为当循环计数器j最终达到0xffffffff'ffffffff时,将其递增到0,这意味着循环不变量j <= n始终满足,循环永远不会退出.

严格地说,用0xffffffff'ffffffff infamously调用sum1会触发未定义的行为,尽管这不是因为溢出本身,而是因为UB([intro.progress]/1)中有无限循环,没有外部可见的副作用.这就是为什么我将[[gnu::noinline]]属性应用于函数,以防止编译器在优化过程中"利用"它.

另一方面,Rust版本不仅定义得很好,而且迭代次数与范围的基数相同:

use std::num::Wrapping;

fn sum1(num: u64) -> u64 {
    let mut sum = Wrapping(0);
    for _ in 0..=num {
        sum += Wrapping(1);
    }
    return sum.0;
}

fn main() {
    println!("{}", sum1(0xffffffff_ffffffff));
}

上述程序(稍加修改,以避免陷入调试与发布模式之和的差异中)将在18446744073709551616次迭代后终止,并打印0;Rust版本小心地维护迭代器状态,以便迭代器中不会发生溢出.

Rust相关问答推荐

WebDriver等待三十四?(Rust Se)

如何go 除铁 rust 中路径组件的第一项和最后一项?

默认特征实现中的生命周期问题

在使用#[NO_STD]时,如何在Rust中收到紧急消息?

无法从流中读取Redis请求

自定义结果枚举如何支持`?`/`FromResidual`?

Pin<;&;mut可能将Uninit<;T>;>;合并为Pin<;&;mut T>;

我是否可以在Ruust中修改 struct 实例上的字符串,以使其在修改后具有相同的字符串生存期?

作为1字节位掩码的布尔值 struct

链表堆栈溢出

如何设置activx websocket actorless的消息大小限制?

使用 Option 来分配?

如何正确使用git2::Remote::push?

为什么我的trait 对象类型不匹配?

trait 对象指针的生命周期

有什么方法可以通过使用生命周期来减轻嵌套生成器中的当生成器产生时borrow 可能仍在使用错误?

Rust:`sort_by` 多个条件,冗长的模式匹配

使用 `clap` 在 Rust CLI 工具中设置布尔标志

火箭整流罩、tokio-scheduler 和 cron 的生命周期问题

当特征函数依赖于为 Self 实现的通用标记特征时实现通用包装器