Rust - The Unit Type

In Rust, the unit type (()) is a special type used in many situations where no specific value is needed.

Bernd Fischer

Bernd Fischer

13. Oct 2024 · 3 min read

Rust - The Unit Type

It represents the concept of "no value" or "empty return" and is the default return type for functions that return nothing. The unit type is often compared to the void type used in many other programming languages but has some peculiarities in Rust.

1. What is the Unit Type ()?

The unit type () is a data type that has exactly one value: (). This value and type represent an empty set of information and are used when no actual value is needed. Unlike some other programming languages, where a special return like void exists, Rust uses the unit type in similar situations.

Example: The Unit Type ()

fn main() {
    let x: () = ();
    println!("Der Wert von x ist: {:?}", x);
}

Output:

Der Wert von x ist: ()

In this example, a value of type () is created and printed. Since there is only one possible value for the unit type, the expression () is always equal to ().

2. Use of the Unit Type in Functions

In Rust, all functions have a return type. If a function returns nothing, it instead returns the unit type (). Rust automatically adds () as the return value if no other return type is explicitly specified.

Example: A Function Without a Return Value

fn keine_rueckgabe() {
    println!("Diese Funktion gibt nichts zurück.");
}

fn main() {
    let result = keine_rueckgabe(); // result hat den Typ `()`
    println!("Rückgabewert: {:?}", result);
}

Output:

Diese Funktion gibt nichts zurück.
Rückgabewert: ()

In this example, the function keine_rueckgabe explicitly returns nothing, but Rust automatically assigns the unit type () as the return value. Thus, the variable result has the type ().

Functions with Explicit Unit Type as Return Value

You can also explicitly specify the unit type as the return value, but this is generally unnecessary because Rust handles this automatically.

fn explizit_unit() -> () {
    println!("Diese Funktion gibt explizit den Unit-Typ zurück.");
}

fn main() {
    explizit_unit();
}

This function also returns the unit type (), although it is explicitly declared in the function signature. This is equivalent to a function with no specified return.

3. The Unit Type in Expressions

The unit type frequently appears in Rust expressions, especially in blocks or control structures where no computation yields an actual value.

Example: Unit Type in if Blocks Without Value

fn main() {
    let condition = true;

    let result = if condition {
        println!("Bedingung ist wahr");
    } else {
        println!("Bedingung ist falsch");
    };

    println!("Rückgabewert des if-Blocks: {:?}", result);
}

Output:

Bedingung ist wahr
Rückgabewert des if-Blocks: ()

In this example, the if block returns the unit type () because within the if and else blocks only the println! macro is called, which also returns ().

Example: The Unit Type in Loops

In Rust loops, which normally do not return (like loop or for), the unit type is also returned unless the loop is explicitly broken with a value.

fn main() {
    let x = for i in 0..5 {
        println!("Zähler: {}", i);
    };

    println!("Der Wert von x ist: {:?}", x);
}

Output:

Zähler: 0
Zähler: 1
Zähler: 2
Zähler: 3
Zähler: 4
Der Wert von x ist: ()

In this case, the loop returns nothing, and the value of x is ().

4. The Unit Type as a Return Value for Errors

Rust also often uses the unit type in functions or structures that can represent some kind of error but do not return an explicit error value. A good example is the function std::result::Result<T, E>, where T is the type of the success value and E is the error value. In some cases, E can take the unit type () to signal that the error is not further specified.

Example: Result with Unit Type as Error

fn operation_mit_fehler() -> Result<i32, ()> {
    // Ein Fehler tritt auf
    Err(())
}

fn main() {
    match operation_mit_fehler() {
        Ok(value) => println!("Erfolgreicher Wert: {}", value),
        Err(_) => println!("Ein Fehler ist aufgetreten"),
    }
}

Output:

Ein Fehler ist aufgetreten

In this example, Err(()) is used to indicate that an error occurred, but there are no detailed error information. This is useful when only success or failure needs to be distinguished without specific error handling.

5. Differences to void in Other Languages

The unit type () in Rust resembles the void type in languages like C or C++. However, there are important differences:

  • In Rust, () is a real type with a value, whereas void in other languages has no value.
  • The unit type can be used like any other value in Rust, for example as a return value of functions, in expressions, or as a placeholder.