Chapter 04

算術演算

とが
とが
2023.09.03に更新

マクロ

前章で登場した println! print! eprintln! eprint! panic! は,全て「マクロ」だといいました.ここでマクロについて少しだけ説明しておきます.

様々なマクロがあり,各々のマクロには何らかの仕事が割り当てられています.たとえば, println! マクロは標準出力に何かを出力して改行するという仕事を持っています.何を出力するかは,その後の丸括弧 () の中に書きます.

マクロを使うときには,必ず後に丸括弧 ( ) か波括弧 { } か角括弧 [ ] を付け,マクロにさせる仕事の詳細な内容をその中に書きます.これをマクロの呼び出しといいます.

println! print! eprintln! eprint! panic! を呼び出すときは,第 21 章で説明する「関数」と見た目を似せるために, 3 種類の括弧のうち丸括弧 ( ) を使うのが普通です.また,括弧内に書く中身のことも,関数に合わせて引数(ひきすう)と呼びます.たとえば,

println!("Hello, world!");

というマクロ呼び出しにおける引数は,文字列リテラル "Hello, world!" です.

値の出力

今までは, println! print! eprintln! eprint! マクロで単に文字列のみを出力していました.しかし,これらのマクロはこんなこともできます.

fn main() {
    println!("最小の素数は{}です.", 2);
}

println! の後の丸括弧の中に,コンマ , で区切って 2 つの引数を書いています. 1 つめの引数は文字列リテラル "最小の素数は{}です" で, 2 つめの引数は整数 2 です.この 2 のようにソースコード中に直接書かれた整数を整数リテラルといいます.

文字列リテラルに着目すると,その中に {} という部分が存在します.これはプレースホルダーと呼ばれます. println! マクロは,プレースホルダーが存在すると,その部分に 2 つめの引数の値を埋め込んで出力します.つまり,今回は {} の部分が 2 で置き換えられて,最小の素数は2です. と出力されることになります.

今回 {} の括弧の中身は空ですが,この中で詳細なフォーマットを指定することもできます.とりあえず今は {} が値で置き換えられるということだけを覚えておいてください.

単に値だけを出力して改行したいときは

fn main() {
    println!("{}", 2);
}

とすれば良いです.これを次のように書くことはできません.

コンパイルエラー
fn main() {
    println!(2);
}

1 つめの引数は文字列リテラルでなくてはなりません.エラーメッセージは

error: format argument must be a string literal
 --> src/main.rs:2:14
  |
2 |     println!(2);
  |              ^
  |
help: you might be missing a string literal to format with
  |
2 |     println!("{}", 2);
  |              ^^^^^

のようになります. Argument は引数, string literal は文字列リテラルの意味です.

次のコードもコンパイルエラーになります.

コンパイルエラー
fn main() {
    println!("{}");
}

今度はプレースホルダーだけがあって,そこに置き換わる値が存在しないからです.エラーメッセージは

error: 1 positional argument in format string, but no arguments were given
 --> src/main.rs:2:15
  |
2 |     println!("{}");
  |               ^^

のようになります.

また,プレースホルダーは複数個あってもかまいません.

fn main() {
    println!("{}の次の完全数は{}です.", 6, 28);
}

今回は「文字列リテラル "{}の次の完全数は{}です."」「整数リテラル 6」「整数リテラル 28」の 3 つの引数があります.1 つ目のプレースホルダーは 6 , 2 つ目のプレースホルダーは 28 で置き換えられ,6の次の完全数は28です.と出力されます.

整数の算術演算

整数に対して演算を行うことができます.ここでは次の 5 つを紹介します.

演算子 意味
+ 足し算
- 引き算または -1
* かけ算
/ 割り算の商
% 割り算のあまり

これらは算術演算と呼ばれます.

たとえばこんなコードが書けます.

fn main() {
    println!("2 + 3 = {}", 2 + 3);
}

このコードでは, println! マクロの引数は「文字列リテラル "2 + 3 = {}" 」と「式 2 + 3 」の 2 つです.式 2 + 3 が計算されて 5 という値になり,文字列リテラルのプレースホルダー {} に埋め込まれて,出力される内容は 2 + 3 = 5 となります.

2 + 3 中の + のように演算を行う記号を,演算子といいます.

一方,文字列リテラル "2 + 3 = {}" はあくまで「2 + 3,…」という文字の並びを意味するため,そこに含まれる 2 + 3 が計算されて 5 になることはありません.文字列について詳しくは第 29 章で説明します.

同様に,引き算やかけ算は

fn main() {
    println!("5 - 2 = {}", 5 - 2); // 3
    println!("3 * 4 = {}", 3 * 4); // 12
}

のようにできます.

式の前に - を付けると正負が逆になります.

fn main() {
    println!("5 + -1 = {}", 5 + -1); // 4
}

式の前に + を付けて +10 のように書くことはできません.

割り算については,次のようになります.

fn main() {
    println!("14を3で割ると,商が{}であまりが{}です.", 14 / 3, 14 % 3);
}

プレースホルダーが 2 つあるので, 1 つ目のプレースホルダーは 14 / 3 の値, 2 つ目のプレースホルダーは 14 % 3 の値で置き換えられます.

14 / 3 は割り算の商(小数点以下を切り捨てた整数値), 14 % 3 は割り算のあまりです.よって今回は 14を3で割ると,商が4であまりが2です. と出力されることになります.

14 / 3\frac{14}{3} = 4.666\ldots にならないことに注意してください.

優先順位

たとえば,

fn main() {
    println!("{}", 5 + 3 * 2);
}

のように,足し算とかけ算が混ざった式を書くと,かけ算の方が先に計算され,結果は 5 + 6 = 11 になります.5 + 3 を先に計算したければ,括弧でくくります.

fn main() {
    println!("{}", (5 + 3) * 2); // 16
}

これを演算子の優先順位といいます. *+ より優先順位が高いということになります.+- の優先順位は同じです.*/% の 3 つの優先順位は同じです.

今後他の演算子も登場しますが,全て優先順位が定まっています.

また,優先順位が同じ演算子が並んでいた場合,結合順序に従って順序が決まります. + - * / % の 5 つは左結合といって,優先順位が同じなら左から順に計算されます.すなわち

fn main() {
    println!("{}", 7 / 2 * 2);
}

は,まず 7 / 2 が計算されて 3 となり,その後に 3 * 2 が計算されて結果は 6 となります.

小数の算術演算

上の演算では整数しか登場しませんでしたが,小数も扱いたい場合があります.小数を扱うというと真っ先に実数を想像するかもしれませんが,コンピュータの内部で全ての実数を正確に表現することはできません.そこで通常は,実数の代わりに浮動小数点数というものを用います.これは無限に続く小数を途中で打ち切って近似するため,誤差が生じる場合があります.これ以降,単に小数というと浮動小数点数のことを指します.

整数の代わりに 1.2 のような小数を書くと,小数を表す浮動小数点数リテラルになります.小数同士でも算術演算を行うことができます.

演算子 意味
+ 足し算
- 引き算または -1
* かけ算
/ 割り算
% 割った余り

10 は整数リテラルですが, 10. は浮動小数点数リテラルです.整数同士,小数同士でないと算術演算は行えないため,2.5 + 10 とは書けず,代わりに 2.5 + 10. と書く必要があります.

注意するのは /% です.

fn main() {
    println!("{}", 6.5 / 2.5);
}

は,普通の割り算が行われて 2.6 と出力されます.一方,

fn main() {
    println!("{}", 6.5 % 2.5);
}

は余りが計算されて 1.5 と出力されます.