Rust中的模运算:不要与取余混淆

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么关于模的问题如此受欢迎?

\n\n

如果你曾经用Rust编写过代码并遇到过负数,你肯定会问过这样一个问题:“模函数在哪里?”。在Stack Overflow上,这个问题获得了数千次浏览,这并非没有原因。关键在于,Rust中的%运算符并不是模运算(modulus),而是取余运算(remainder)。区别很微妙,但至关重要,尤其是在算法、密码学或处理循环结构时。

\n\n

在本文中,我们将探讨数学模与取余的区别,Rust如何处理负数,以及如何编写自己的计算真实模的函数。本文面向初学者,但对希望巩固知识的经验丰富的开发者也会有所帮助。

\n\n

内容概要

\n
    \n
  • 什么是模和余:数学上的区别
  • \n
  • 为什么Rust中的%不是模运算
  • \n
  • 如何在Rust中编写模函数
  • \n
  • 现实生活中的例子:循环索引和哈希
  • \n
  • 结论和实用建议
  • \n
\n\n

数学:模 vs 余

\n\n

让我们从基础开始。在数学中,模(modulus)是一种返回小于除数的非负数的运算。例如:

\n
    \n
  • 7 mod 3 = 1(7除以3余1)
  • \n
  • -7 mod 3 = 2(因为-7 = 3 * (-3) + 2)
  • \n
\n\n

取余(remainder)是整数除法后剩下的部分。在大多数编程语言(C、C++、Java、Rust)中,%运算符返回的就是余数,如果被除数为负,余数也可能为负。例如:

\n
    \n
  • 7 % 3 = 1(一致)
  • \n
  • -7 % 3 = -1(在Rust中,因为-7 / 3 = -2,余数为-1)
  • \n
\n\n

为什么这很重要?想象一下,你正在编写一个时钟程序:时间范围0–23。如果现在是22点,你加上5小时,会得到凌晨3点。这可以通过模运算实现:(22 + 5) mod 24 = 3。但如果使用取余,当值为负数时就会出错。例如,在Rust中(-3) % 24会得到-3,而不是21。

\n\n

为什么Rust中的%运算符是取余

\n\n

在Rust中,%运算符被定义为取余运算。这在文档和标准库的行为中都有明确规定。原因在于历史沿袭:C语言和许多其他语言都是这样做的。Rust遵循“显式优于隐式”的原则,但在这里新手常常会掉入陷阱。

\n\n
fn main() {\n    println!("7 % 3 = {}", 7 % 3);   // 1\n    println!("-7 % 3 = {}", -7 % 3); // -1\n    println!("7 % -3 = {}", 7 % -3); // 1(是的,除数的符号不影响结果)\n    println!("-7 % -3 = {}", -7 % -3); // -1\n}
\n\n

请注意:结果的符号始终与被除数的符号一致。这是不是模运算。对于真正的模运算,结果必须始终为非负数。

\n\n

如何在Rust中编写真正的模函数?

\n\n

Rust标准库没有提供内置的模函数(与Python不同,Python中的%就是模运算)。但编写一个非常简单。以下是最常见的方法——通过取余并修正。

\n\n

整数的模函数

\n\n
fn modulus(a: i32, b: i32) -> i32 {\n    let r = a % b;\n    if r < 0 { r + b.abs() } else { r }\n}\n\nfn main() {\n    println!("modulus(7, 3) = {}", modulus(7, 3));   // 1\n    println!("modulus(-7, 3) = {}", modulus(-7, 3)); // 2\n    println!("modulus(7, -3) = {}", modulus(7, -3)); // 1(模始终为非负数)\n    println!("modulus(-7, -3) = {}", modulus(-7, -3)); // 2\n}
\n\n

工作原理:我们取余数a % b。如果结果为负,则加上除数的绝对值

博客

书籍推荐