Aleff
Aleff

Reputation: 257

What does () mean as an argument in a function where a parameter of type T is expected?

I am new to Rust and I was reading the Dining Philosophers' tutorial when I found this:

Mutex::new(())

I don't know what the argument inside new means. I read the documentation for Mutex and I still have no idea what it means. I would appreciate an explanation about what is happening under the hood.

Upvotes: 6

Views: 331

Answers (2)

Brian Campbell
Brian Campbell

Reputation: 333146

() is simply a tuple with no values; a 0-tuple. The type and the value are spelled the same, both (). The type is sometimes known as the "unit type"; it used to actually be a distinct type in the compiler, but now is just treated as a degenerate tuple. It is a 0-sized type; objects of this type won't ever actually take up any space, though it is a Sized type, just with a size of 0.

It is used for cases where you need to have a value or a type, but you have nothing relevant to put there. For instance, if you have a function that doesn't return a value, and call it in a place that expects a value, you find that it actually returns the value () of type ().

fn nothing() {}

fn main() {
    println!("{:?}", nothing());
}

That prints () (playpen).

Another use is when you have a generic type like Result<T, E>, which indicates a success or failure of some operation, and can hold either the the result of the successful operation, or an error indicating why it failed. Some operations, such as std::io::write which have no value to return if successful but want to be able to indicate an error, will return a std::io::Result<()>, which is actually a synonym for Result<(), std::io::Error>; that allows the function to return Ok(()) in the success case, but some meaningful error when it fails.

You might compare it to void in C or C++, which are also used for a lack of return value. However, you cannot ever write an object that has type void, which makes void much less useful in generic programming; you could never have an equivalent Result<void, Error> type, because you couldn't ever construct the Ok case.

In this case, a Mutex normally wraps and object that you want to access; so you can put that object into the mutex, and then access it from the guard that you get when you lock the mutex. However, in this example there is no actual data being guarded, so () is used since you need to put something in there, and Mutex is generic over the type so it can accept any type.

Upvotes: 8

George Hilliard
George Hilliard

Reputation: 15952

() is the empty tuple, also called the unit type -- a tuple with no member types. It is also the only valid value of said type. It has a size of zero (note that it is still Sized, just with a size of 0), making it nonexistent at runtime. This has several useful effects, one of which is being used here.

Here, () is used to create a Mutex with no owned data -- it's just an unlockable and lockable mutex. If we explicitly write out the type inference with the turbofish operator ::<>, we could also write:

Mutex::<()>::new( () )

That is, we're creating a new Mutex that contains a () with the initial value ().

Upvotes: 9

Related Questions