空のタプルは別名ユニット型 (unit type) と呼ばれる。
これは一見すると使いようがないが、実は各所で利用されている。
空のタプル ()
がコード中に明示的に登場しないケースについて。
文 (セミコロン ‘;
’ で終る式) は、ユニット型を生成する。
以下では、f1
の呼出結果は数値になるが、f2
の呼出結果はユニットになる。
fn main() {
let f1 = || { add_and_print(1, 1) };
let f2 = || { add_and_print(1, 1); };
assert_eq!(f1(), 2);
assert_eq!(f2(), ());
}
fn add_and_print(x: i32, y: i32) -> i32 {
let result = x + y;
println!("{result}");
result
}
戻り値のない関数は、空のタプルを戻り値とする関数の糖衣構文である。
以下では、do_something1
と do_something2
は同じ意味になる。
fn main() {
do_something1();
do_something2();
}
fn do_something1() {
println!("...");
}
fn do_something2() -> () {
println!("...");
}
空のタプル ()
がコード中に明示的に登場するケースについて。
ユニット型により補足情報がない事を示す。
以下では、関数 div
の成功時には計算結果を伴うものの、失敗時には補足情報がない。そのため、Result<T, E>
の型パラメタ E
にユニット型を指定している。
fn main() {
assert_eq!(div(7, 3), Ok(2));
assert_eq!(div(7, 0), Err(()));
}
fn div(x: i32, y: i32) -> Result<i32, ()> {
if y != 0 {
Ok(x / y)
} else {
Err(())
}
}
ユニット型により不要な型パラメタを無視できる。
以下では、キーバリューマップである HashMap<K, V>
型から、キーのみを管理するセット型 KeySet
を実装している (つまり、HashSet<T>
風の機能を自前で実装している)。
use std::collections::HashMap;
type KeySet<K> = HashMap<K, ()>;
fn main() {
let mut set = KeySet::new();
set.insert(2, ());
set.insert(3, ());
set.insert(5, ());
assert!(set.contains_key(&3));
}
ユニット型によりフィールドのない型についても、コンストラクタの使用を強制できる。
以下では、MyType
にユニット型のダミーフィールド _private
を追加し、new
関数なしでその値を作成できないようにしている。これにより、MyType {}
のようなコードが禁止され、将来的に MyType
にフィールドが追加された時のエラーを予防できる。
fn main() {
let my_type = my::MyType::new();
my_type.do_something();
}
mod my {
pub struct MyType {
_private: ()
}
impl MyType {
pub fn new() -> Self {
Self { _private: () }
}
pub fn do_something(&self) {
println!("...");
}
}
}