What is the purpose of the unit type in Rust?
Asked Answered
G

4

86

Rust has the unit type, (), a type with a single zero-size value. The value of this unit type is also specified using ().

What is the purpose of the unit type and its value? Is it a mechanism to avoid using null (or nil) like other languages have?

Gutturalize answered 19/7, 2014 at 16:4 Comment(3)
It's more like void in C-like languages. As far as I know, everything is an expression in rust, so () is the value returned by something which is only executed for its effects.Baier
You can make your own zero-size types, just like () by saying struct MyNewZeroSizeType; It can be useful ("marker types"!)Brigandine
It's doesn't seem like null in java because if you say your returning an int you can't return () in place of it.Absorbent
S
116

() is a value of the type () and its purpose is to be useless.

Everything in Rust is an expression, and expressions that return "nothing" actually return (). The compiler will give an error if you have a function without a return type but return something other than () anyway. For example

fn f() {
    1i32 // error: mismatched types: expected `()` but found `int`
}

There are practical uses for () too. Sometimes we don't care about a generic type, and () makes this explicit.

For example, a Result<(), String> can be used as return type for a function that either completes successfully or fails for a variety of reasons.

Swearingen answered 19/7, 2014 at 17:32 Comment(2)
In suppose one can also define methods for it, but there will be no state associated with those... Is that correct?Tremor
@Tremor yes, pretty much everything you can do with () has a trivial implementation.Symploce
L
54

If you're coming from a C-like language (C, C++, Java, etc.), you can think of unit as being like void. It is the type you return when you don't want to return anything.

Type theorists will point out that unit is not like void, because unit has exactly 1 value whereas void has 0 values.

In practice, the amount of information you can store in both types is the same (0 bits), although languages that use unit tend to be nicer to work with because you can treat it as you would any other value.

You can store it in a variable, struct, collection, or anywhere else you could store a value. You can pass as an argument or return it as a result. You can create a reference to it. Etc.

So, when is that useful? Mainly when you don't care what type of value you're dealing with. It means you can write polymorphic/generic code without needing to worry about whether the value you are dealing with actually contains any information, that is, no need of a special case for whether you're storing actual data or ().

One example of where this is used is in HashSet. A HashSet<T> is actually implemented as a thin wrapper around HashMap<T, ()>. (A map from the generic type T to ())

Libelous answered 19/2, 2016 at 4:6 Comment(2)
another area of use is in Dynamic Dispatch where the TraitObject implementation documented here has the *mut () type for data and vtable.Fisher
I'd add that void and () are also very useful not just when you don't care about the result, but when there is no result. I might have a method which stores to a file and either fails with an error or just completes successfully with no further information -- Result<(), MyError>. You could argue that Option<MyError> is 'better', but I disagree; the Result's naming conventions mean it's explicitly clear what's going on.Tiebout
U
3

Unit type () is used when you have code that does not return anything. from the docs

The () type, also called “unit”.

The () type has exactly one value (), and is used when there is no other meaningful value that could be returned.

1- function that is not returning anything

fn test()->() {}

2- code block that does not return anything:

// in this case type will be let x:()={codeblock}
let x={
        println!("testing code block")
    };
  • since each unit type has exactly one value, all unit type variables are equal. for example

    fn main(){
       let x=();
       let y=println!("test");
       // this will print out true
       println!("Are x and y variables equal? / {}",x==y);
    }
    

3- it is commonly used in Result enum where you are not returning anything but want to write type for successful execution. example from docs:

use std::error::Error;
use std::fs::File;

fn main() -> Result<(), Box<dyn Error>> {
    let greeting_file = File::open("hello.txt")?;

    Ok(())
}
Underact answered 22/1, 2023 at 1:0 Comment(0)
M
1

The unit type is just a tuple with no fields, it wasn't specifically created for being useless. But when used, it is intended to be useless. When you don't specify the return value of a function, it is a unit and any block that can evaluate to a value (like a function, if, else, etc), but doesn't, actually evaluates to a unit as well.

Mix answered 28/2 at 14:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.