前言
在程式語言中,運算子通常會用符號而非文字來表示。通常運算子無法拆分成更小的部分,可視為程式語言的基本指令。本章介紹 Rust 的運算子,這些運算子是 Rust 中實際執行工作的基礎元件。
Rust 包括以下運算子:
- 代數運算子
- 二元運算子
- 比較運算子
- 布林運算子
- 轉型運算子
- 指派運算子
- 複合指派運算子
- 其他運算子
本文將逐一介紹這些運算子。
代數運算子
包括以下運算子:
+
:加-
:減*
:乘/
:除%
:取餘數
見以下短例:
fn main() {
assert_eq!(2 + 1, 3);
assert_eq!(5 - 1, 4);
assert_eq!(2 * 3, 6);
assert_eq!(6 / 3, 2);
}
在本例中,我們使用 assert_eq!
代替 println!
,這個函式在程式兩邊不相等時會引發錯誤,好處是由程式替我們檢查值是否正確,而不需人工確認。本書中部分代碼會使用這種風格來表示。
然而,由於電腦的限制,浮點數在電腦內的表示是不精確的。例如,0.3 - 0.2 的結果 不會剛好是 0.1 而有一個微小的誤差。見以下範例:
fn main() {
assert_eq!(0.3 - 0.2, 0.1);
}
本程式引發了以下錯誤,表示浮點數的運算是不精確的:
thread 'main' panicked at 'assertion failed: `(left == right)` (left: `0.09999999999999998`, right: `0.1`)'
比較好的方法,是比較兩數相減的誤差值,當誤差值的絕對值夠小時,表示條件成立。見以下範例:
use std::f64;
fn main() {
assert!(f64::abs((0.3 - 0.2) - 0.1) < 1e-10);
}
本程式即可順利執行。在本程式中,我們使用 assert!
,這個函式在其內條件式不為真時引發錯誤,使用方式類似 assert_eq!
。
二元運算子
包括以下運算子:
&
:bitwise AND|
:bitwise inclusive^
:bitwise exclusive OR<<
:左移>>
:右移
二元運算是以二進位數來運算,和平常使用的十進位數不同,有興趣的讀者可參考計算機概論等相關書籍。以下是短例:
fn main() {
assert_eq!(0b0011u32 & 0b0101, 0b0001);
assert_eq!(0b0011u32 | 0b0101, 0b0111);
assert_eq!(1u32 << 5, 32);
}
平常我們較少使用二元運算,因為比較不直觀。但二元運算的速度比十進位運算快,對速度有所要求時可以考慮使用。
比較運算子
對於支援的型別,也可比較其大小。包括
==
:等於!=
:不等於>
:大於<
:小於>=
:大於等於<=
:小於等於
以下是短例:
fn main() {
assert!(2 + 3 == 5);
assert!(2 + 3 != 4);
assert!(4 + 1 > 3);
assert!(4 + 1 < 7);
assert!(3 + 2 >= 4);
assert!(3 + 2 <= 6);
}
布林運算子
布林運算子是用來結合兩個以上的條件式,包括
&&
:AND||
:OR!
:NOT
AND 運算遵守以下邏輯:
p | q | Result |
---|---|---|
true | true | true |
true | false | false |
false | true | false |
false | false | false |
OR 運算遵守以下邏輯:
p | q | Result |
---|---|---|
true | true | true |
true | false | true |
false | true | true |
false | false | false |
NOT 運算遵守以下邏輯:
p | Result |
---|---|
true | false |
false | true |
如果覺得記上述表格很困難,只要記得「所有條件為真時,AND 才為真;只要有條件為真時,OR 即為真」。結合上述基本邏輯,可撰寫更複雜的條件敘述。
以下為範例:
fn main() {
assert_eq!(true && true, true);
assert_eq!(true && false, false);
assert_eq!(false && true, false);
assert_eq!(false && false, false);
assert_eq!(true || true, true);
assert_eq!(true || false, true);
assert_eq!(false || true, true);
assert_eq!(false || false, false);
assert_eq!(!true, false);
assert_eq!(!false, true);
}
轉型運算子
轉型運算子 as 是用來轉換資料的型別。由於 Rust 的安全設計,不能直接用整數和浮點數相互運算,而要透過明確的轉型,這和大部分的程式語言不同。下列程式看似正確:
use std::f64;
fn main() {
assert!(f64::abs(1.0 - 1) < 1e-10);
}
本程式卻引發了以下錯誤:
error[E0277]: the trait bound `{float}: std::ops::Sub<{integer}>` is not satisfied
這個錯誤訊息,包含一個新的概念。在 Rust,運算子是透過 trait 的機制來達成,若沒有實作相關的 trait,則無法進行相關的運算。我們會在後續的章節介紹 trait。
我們將程式改寫如下:
use std::f64;
fn main() {
assert!(f64::abs(1.0 - (1 as f64)) < 1e-10);
}
經過轉型,本程式為 f64
型別間的運算,即可正確執行。
指派運算子
我們已經在前一章看過指派運算子 =
了,在宣告變數時通常也會一併賦值。
fn main() {
let x = 3;
}
注意:不要將指派運算子 =
和相等運算子 ==
搞混。
複合指派運算子
複合指派運算子是將代數運算子或二元運子算以及指派運算子合併,簡化程式碼。例如,以下程式碼:
fn main() {
let mut x = 3;
x = x + 1;
assert_eq!(x, 4);
}
可以簡化為:
fn main() {
let mut x = 3;
x += 1; // Compound assignment
assert_eq!(x, 4);
}
其他運算子
這裡列出其他筆者未提到的運算子:
- 負號運算子
-
:將數字的正負號反轉 - 解參考運算子
*
:得到參考所指向的值 - 參考運算子
&
和& mut
:得到某個值的參考
我們會在後續的章節討論參考 (reference)。
註:Rust 的參考類似 C 或 C++ 的指標。
運算子優先順序
Rust 運算子的優先順序,由高至低,如下:
as :
* / %
+ -
<< >>
&
^
|
== != < > <= >=
&&
||
.. ...
<-
=
筆者不會刻意去記運算子的優先順序。只要在程式碼中減少過度複雜的敘述,即可減少因運算子優先順序造成的混淆。如果某些敘述較複雜,用中括號 () 將運算優先順序提高即可。