Rustの演算子と式:算術演算からビット演算までを網羅したガイド
Rustでは、
ifやmatchを含め、ほぼすべてが式です。
式(値を返すもの)と文(値を返さないもの)の違いを理解することは、簡潔なRustコードを書くための鍵となります。
1. 学習内容
- 算術演算子:
+ - * / % - 比較演算子:
== != < > <= >= - 論理演算子:
&& || ! - ビット演算子:
& | ^ << >> - 式と文の違い
- 演算子の優先順位
2. レジ係の物語
(1) もどかしさ:割引の計算をするたびに間違いをしてしまう
トムは衣料品店の店長で、毎日さまざまな割引の計算をしなければなりません:
- 2点購入で20%オフ、3点購入で30%オフ
- 500以上のご注文で50割引
- 会員はさらに5%割引となります
- 割引を組み合わせる際に順序を間違えてしまったため、月末の帳簿が合わず、500元の損失が出てしまいました。
「正しい順序で自動的に計算を行ってくれる電卓があれば、最高なのに……」
(2) Rustの式体系
fn main() {
let price: f64 = 299.0; // A piece of clothing 299 yuan
let quantity: i32 = 3; // Bought 3 items
let is_member: bool = true; // Is a member
// Calculate all discounts with a single expression
let total = (price * quantity as f64) // Original Price
* if quantity >= 3 { 0.7 } else { 0.9 } // Volume Discount
* if is_member { 0.95 } else { 1.0 }; // Member Discounts
println!("Original Price: {:.2} yuan", price * quantity as f64);
println!("Price after discount: {:.2} yuan", total);
println!("Save: {:.2} yuan", price * quantity as f64 - total);
}
Rustでは、
ifはそれ自体が式であり、直接値を返すことができます。これにより、割引の計算を単一の式の連鎖として表現することができ、エラーのリスクを排除できます。
3. 演算子のリスト
graph TB
A[Rust Operators] --> B[Arithmetic: + - * / %]
A --> C[Comparison: == != < > <= >=]
A --> D[Logic: && || !]
A --> E[Bitwise Operations: & | ^ << >>]
A --> F[Assignment: = += -= *= /=]
(1) 算術演算子
| 演算子 | 例 | 説明 |
|---|---|---|
+ |
a + b |
追加 |
- |
a - b |
引き算 |
* |
a * b |
乗算 |
/ |
a / b |
除算(整数切り捨て) |
% |
a % b |
残りの部分 |
- |
-a |
負の値(単項) |
整数除算では切り捨てが行われます:
5 / 2 = 2(2.5ではありません)。浮動小数点数の結果を得るには、浮動小数点型を使用する必要があります:5.0 / 2.0 = 2.5。
(2) 比較演算子
すべての比較演算子は、bool 型の値(true または false)を返します。
let a = 10;
let b = 20;
println!("{}", a == b); // false
println!("{}", a != b); // true
println!("{}", a < b); // true
(3) 論理演算子
| 演算子 | 名称 | 例 | 説明 |
|---|---|---|---|
&& |
論理 AND | a && b |
両方が真の場合にのみ真(短絡評価) |
| ` | ` | 論理OR | |
! |
論理NOT | !a |
反転 |
短絡評価:
&&—左辺が偽の場合、右辺は評価されない;||—左辺が真の場合、右辺は評価されない。これは「アクセス前のチェック」パターンで使用できる。
(4) ビット演算子
| 演算子 | 名称 | 例 | 説明 |
|---|---|---|---|
& |
ビット単位のAND | a & b |
対応するすべてのビットが1の場合にのみ1となる |
| ` | ` | ビット単位のOR | `a |
^ |
ビット単位のXOR | a ^ b |
対応するビットが異なる場合にのみ1となる |
<< |
左シフト | a << n |
nビット左シフト(2^n倍に相当) |
>> |
右シフト | a >> n |
nビット右シフト(2^nで割ることに相当) |
4. 式と文
(1) 主な相違点
| 概念 | 定義 | 例 |
|---|---|---|
| 式 | 戻り値があるか | 5 + 3 は 8 を返し、if true { 1 } else { 0 } は 1 を返す |
| ステートメント | 戻り値なし | let x = 5; 戻り値なし、fn foo() {} 戻り値なし |
Rustでは、セミコロンによって、それが式なのか文なのかが決まります:
fn main() {
let y = {
let x = 3;
x + 1 // No semicolons -- This is an expression, Returns 4
}; // End of semicolon let Statement
println!("y the value: {}", y); // 4
}
(2) ブロック式
{} ブロック内のコードブロックは式とすることができます。最後の行がセミコロンで終わっていない場合、そのブロックは値を返します:
let result = {
let a = 2;
let b = 3;
a * b // No semicolons, This expression returns 6
};
5. 演算子の例
(1) ▶ サンプル:算術演算と型(難易度 ⭐)
// ============================================
// Demonstration of Arithmetic Operators——Note: Truncation in integer division
// ============================================
fn main() {
let a = 10;
let b = 3;
println!("Addition: {} + {} = {}", a, b, a + b);
println!("Subtraction: {} - {} = {}", a, b, a - b);
println!("Multiplication: {} * {} = {}", a, b, a * b);
println!("Integer Division: {} / {} = {} (Truncation)", a, b, a / b);
println!("Floating-Point Division: {} / {} = {:.2}", a as f64, b as f64, a as f64 / b as f64);
println!("Modulo: {} % {} = {}", a, b, a % b);
}
出力:
Addition: 10 + 3 = 13
Subtraction: 10 - 3 = 7
Multiplication: 10 * 3 = 30
Integer Division: 10 / 3 = 3 (Truncation)
Floating-Point Division: 10 / 3 = 3.33
Modulo: 10 % 3 = 1
asキーワードは型変換に使用されます。a as f64浮動小数点数の結果を得るには、除算を行う前に整数を浮動小数点数に変換してください。
(2) ▶ サンプル:論理演算の短絡評価(難易度:⭐⭐)
// ============================================
// Short-Circuit Evaluation of Logical Operators
// ============================================
fn main() {
let age = 17;
let has_id = true;
// && Short Circuit: If the left is false, the right side is not executed
let can_buy = age >= 18 && has_id;
println!("Available for purchase (&&): {}", can_buy); // false (Since the left side is already false)
// || Short Circuit: If the left is true, the right side is not executed
let is_ok = true || (expensive_check());
println!("(|| Short Circuit, The right side will not be executed): {}", is_ok);
}
fn expensive_check() -> bool {
println!("This function will not be executed!");
true
}
出力:
Available for purchase (&&): false
(|| Short Circuit, The right side will not be executed): true
ショートサーキット評価は、パフォーマンスを最適化するための重要な手法です。これは、不要な計算を避けるために、コストの低いチェックを左側に、コストの高いチェックを右側に配置するものです。
(3) ▶ サンプル:式ブロックの戻り値 (難易度 ⭐⭐)
// ============================================
// Using Expression Blocks to Implement Concise Assignments with Complex Conditions
// ============================================
fn main() {
let score = 85;
let grade = {
if score >= 90 {
"A"
} else if score >= 80 {
"B"
} else if score >= 70 {
"C"
} else if score >= 60 {
"D"
} else {
"F"
}
}; // Note: The entire if-else chain is an expression, End with a semicolon let Statement
println!("Score: {}, Level: {}", score, grade);
// Alternatives to Ternary Operations: Use if Expression
let is_pass = if score >= 60 { "passed" } else { "failed" };
println!("Result: {}", is_pass);
}
出力:
Score: 85, Level: B
Result: passed
Rustには三項演算子(
condition ? a : b)はありませんが、ifはそれ自体が式であり、より読みやすい形で同じ結果を得ることができます。
(4) ▶ サンプル:ビット演算の実践――アクセス権フラグと色の混合(難易度 ⭐⭐)
// ============================================
// Bitwise Operations in Practice:File Permissions and RGB Color Manipulation
// ============================================
fn main() {
let read_perm: u8 = 0b100; // 4
let write_perm: u8 = 0b010; // 2
let exec_perm: u8 = 0b001; // 1
let user_perm = read_perm | write_perm;
println!("User Permissions: {:03b} (Read+Write)", user_perm);
let full_perm = read_perm | write_perm | exec_perm;
println!("Full Permissions: {:03b} (Read+Write+Execute)", full_perm);
let can_read = (user_perm & read_perm) != 0;
let can_exec = (user_perm & exec_perm) != 0;
println!("User-readable: {}, Executable: {}", can_read, can_exec);
let no_write = user_perm & !write_perm;
println!("Remove Write Permissions: {:03b}", no_write);
let toggled = user_perm ^ exec_perm;
println!("Toggle the execution bit: {:03b}", toggled);
let red: u32 = 0xFF0000;
let green: u32 = 0x00FF00;
let blue: u32 = 0x0000FF;
let yellow = red | green;
let white = red | green | blue;
println!("\nRed: {:06X}", red);
println!("Yellow (Red|Green): {:06X}", yellow);
println!("White (Red|Green|Blue): {:06X}", white);
let pixel: u32 = 0xFF8040;
let r = (pixel >> 16) & 0xFF;
let g = (pixel >> 8) & 0xFF;
let b = pixel & 0xFF;
println!("\nPixel #FF8040 → R={}, G={}, B={}", r, g, b);
let shift_val: u8 = 1;
println!("\n1 << 0 = {}", shift_val << 0);
println!("1 << 1 = {}", shift_val << 1);
println!("1 << 2 = {}", shift_val << 2);
println!("1 << 3 = {}", shift_val << 3);
}
出力:
User Permissions: 110 (Read+Write)
Full Permissions: 111 (Read+Write+Execute)
User-readable: true, Executable: false
Remove Write Permissions: 100
Toggle the execution bit: 111
Red: FF0000
Yellow (Red|Green): FFFF00
White (Red|Green|Blue): FFFFFF
Pixel #FF8040 → R=255, G=128, B=64
1 << 0 = 1
1 << 1 = 2
1 << 2 = 4
1 << 3 = 8
ビット演算はシステムプログラミングにおいて極めて一般的です。権限管理では、
|で結合、&でチェック、& !で削除を行い、色処理では、|でブレンド、>>と&でチャンネルを抽出します。<<は、2の累乗による乗算に相当します。
(5) ▶ サンプル:演算子の優先順位と複合式(難易度 ⭐⭐⭐)
// ============================================
// Operator Precedence in Practice + Compound Assignment Operator
// ============================================
fn main() {
println!("=== Operator Precedence ===");
let a = 2 + 3 * 4;
println!("2 + 3 * 4 = {} (Multiply First, Then Add)", a);
let b = (2 + 3) * 4;
println!("(2 + 3) * 4 = {} (Parentheses Take Precedence)", b);
let c = 10 - 6 / 2;
println!("10 - 6 / 2 = {} (Multiply First, Then Subtract)", c);
let d = true && false || true;
println!("true && false || true = {} (&& Take precedence over ||)", d);
let e = true || false && false;
println!("true || false && false = {} (&& Take precedence over ||)", e);
println!("\n=== Compound Assignment Operator ===");
let mut score: i32 = 100;
score += 20;
println!("score += 20 → {}", score);
score -= 30;
println!("score -= 30 → {}", score);
score *= 2;
println!("score *= 2 → {}", score);
score /= 7;
println!("score /= 7 → {}", score);
score %= 3;
println!("score %= 3 → {}", score);
println!("\n=== Chain Comparison(Using logical operators)===");
let x = 50;
let in_range = x >= 0 && x <= 100;
let out_range = x < 0 || x > 100;
println!("x={}, in [0,100]: {}, Out of range: {}", x, in_range, out_range);
let charlie_score = 75;
let bob_score = 88;
let both_passed = charlie_score >= 60 && bob_score >= 60;
let any_excellent = charlie_score >= 90 || bob_score >= 90;
println!("Both passed: {}, Someone excellent: {}", both_passed, any_excellent);
}
出力:
=== Operator Precedence ===
2 + 3 * 4 = 14 (Multiply First, Then Add)
(2 + 3) * 4 = 20 (Parentheses Take Precedence)
10 - 6 / 2 = 7 (Multiply First, Then Subtract)
true && false || true = true (&& Take precedence over ||)
true || false && false = true (&& Take precedence over ||)
=== Compound Assignment Operator ===
score += 20 → 120
score -= 30 → 90
score *= 2 → 180
score /= 7 → 25
score %= 3 → 1
=== Chain Comparison(Using logical operators)===
x=50, in [0,100]: true, Out of range: false
Both passed: true, Someone excellent: false
演算子の優先順位(高い順):
* / %→+ -→ 比較演算子 →&&→||。迷った場合は括弧を使用してください。「簡潔さ」よりもコードの可読性を優先してください。
❓ よくある質問
as を使用した型変換における注意点は何ですか?as は、エラーを報告せずにデータを黙って切り捨てます。📖 まとめ
- Rustには、算術演算子、比較演算子、論理演算子、ビット演算子の4種類の演算子があり、そのほとんどは他の言語のものと一致しています。
- 整数除算では切り捨てが行われます。
asを使用して、浮動小数点除算に変換してください。 - 論理演算子
&&および||は、短絡評価の挙動を示す - 式には戻り値がありますが、文にはありません。この違いは、セミコロンがあるかどうかで決まります。
ifはそれ自体が式であり、他の言語における三項演算子の代わりとして使用できます Rust には++/--はありません。代わりに+= 1/-= 1を使用してください。
📝 練習問題
- 難易度 ⭐:
as変換を使用して、i32型の5および2をf64に変換し、5 / 2 = 2.5を出力するプログラムを作成してください。 - 難易度 ⭐⭐:変数
x = 5を定義し、式ブロック{ x + 1 }を使用してyに値を代入し、式ブロックの外側でもxにアクセスできるかどうかを判断してください。 - 難易度 ⭐⭐⭐: ビット演算子を使用して関数
fn is_even(n: i32) -> boolを実装してください。この際、パリティを判定するには&のビット演算(%ではない)を使用する必要があります。



