Rust、Go 与 Ruby 中循环范围语义差异详解

本文解析 rust 的 `..` 半开区间(左闭右开)与 go/ruby 中常见闭区间逻辑的差异,指出因边界处理不一致导致求和结果不同的根本原因,并提供跨语言对齐的正确写法。

Rust 的范围表达式(如 0..n)默认为左闭右开区间,即包含起始值 0,但不包含结束值 n。因此,0is..100000000is 实际遍历的是整数 0, 1, 2, ..., 99999999 —— 共 100,000,000 个数,最大值为 99999999。

而你在 Go 和 Ruby 示例中使用的逻辑实质上是 1..99999999(Go 的 i 跳过了 99999999,实际求和范围是 1 到 99999998(含),共 99,999,998 个数,自然得到更小的结果 4999999850000001。

要使三门语言计算完全相同的数学和(即 0 + 1 + 2 + ... + 99999999 = 4999999950000000),需统一范围语义:

✅ 正确对齐写法(全部计算 0..100000000 或 0 ≤ i ≤ 99999999):

  • Rust(当前已正确):

    fn main() {
        let mut sum = 0i64; // 使用 i64 避免溢出(0..100_000_000 的和 ≈ 5e15)
        for i in 0..100_000_000 {
            sum += i;
        }
        println!("{}", sum); // 输出:4999999950000000
    }
  • Go(修正为包含 99999999):

    package main
    
    import "fmt"
    
    func main() {
        var sum int64 = 0
        for i := int64(0); i < 100000000; i++ { // 或 i <= 99999999
            sum += i
        }
        fmt.Println(sum) // 输出:4999999950000000
    }
  • Ruby(修正为包含 99999999):

    sum = 0
    (0...100_000_000).each { |i| sum += i }
    puts sum # 输出:4999999950000000

⚠️ 注意事项:

  • Rust 中旧语法 0is 已废弃(is 不是合法类型后缀),应使用 i32、i64 等显式类型,且此处必须用 i64(i32 最大值仅约 21 亿,远小于求和结果);
  • Go 默认 int 在 64 位系统可能为 int64,但为可移植性,建议显式声明 int64;
  • Ruby 的 Bignum 自动处理大整数,无需手动指定类型,但范围写法 ...(右开)与 ..(右闭)需严格区分。

总结:差异并非“数学错误”,而是语言设计对区间语义的约定不同。Rust 坚持半开区间的工程惯例(与数组索引、切片保持一致),而 Go/Ruby 示例误用了不匹配的边界条件。统一范围定义后,三者结果完全一致 —— 这正是理解底层语义比盲目对照输出更重要的原因。