空のタプルは別名ユニット型 (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_something1do_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!("...");
        }
    }
}