Generic function returns explained for JS people?


#1

Yesterday I tried the examples of the Rust book. Nice and short even if I still didn’t get the end of the Thread example.

Anyway.

I coded it down and saw the Mutex::new(()) and thought, well lets try to write a function on your own.

So I wrote:

fn new_fork() -> Mutex {
    Mutex::new(())
}

But the compiler didn’t like it, because Mutex is a generic type.

So I changed it to:

fn new_fork() -> Mutex<_> {
    Mutex::new(())
}

Because I don’t care about what’s “in” the Mutex, also I have no idea what the type of () is, since it’s an empty tuple and I only learned that a tuples type is defined by the types of the data in it.

But I got denied again. So I’m already failing at a basic function :worried:

How do I find out which type the constructs I’m using, but didn’t write myself, deliver?


#2

The type of () is simply () and it’s usually called the unit type. The return type of your function would then be Mutex<()>.

Primitive types, including (but not exclusively) &T, u8, f64, tuples (ex. (A, B, C)), [T; N] and [T], are the only types with “special” signatures, while structs, enums, traits and typedefs (aliases) follow the pattern Name<'a, 'b, ..., A, B, ...>, where 'a, 'b, etc. are lifetime parameters and A, B, etc. are type parameters. A type without any parameter are simply written as Name. There are also associated types, like Item=T in &Iterator<Item=T>, but those are a little special.


#3

You did write it yourself, it has a () in the mutex, therefore, it’s a
Mutex<()>

See http://is.gd/q1mq6W


#4

Oh so the type is the same as the value then? Thank you :slight_smile:


#5

Please be aware that this is a special property of the () type (sometimes pronounced ‘unit’)!

If you were to write Mutex::new(42) it’d be of type Mutex<i32>.


#6

Not really. It’s an a empty tuple. There is no unit type (anymore).


#7

You can find out here.

Somewhere in the standard library, there is something similar to this written:

struct Mutex<T> {
    // implementation details go here
}

impl<T> Mutex<T> {
    fn new(t: T) -> Mutex<T> {
        // implementation details go here
    }
}

This means that the library provides one Mutex type for each type T. For example, if you want to use a Mutex with i32 as T (in other words, Mutex<i32>). The compiler transforms the code above into roughly this:

struct Mutex<i32> { // this line is invalid in "real" rust!
    // implementation details go here
}

impl Mutex<i32> {
    fn new(t: i32) -> Mutex<i32> {
        // implementation details go here
    }
}

Which means that Mutex::<i32>::new(...) (that ugly syntax is the least of evils) takes an i32 and returns Mutex<i32>. But Mutex::<i32>::new(...) can usually be “abbreviated” as Mutex::new(...) and the compiler can sometimes figure out (aka type inference) that it’s i32.

FYI, () is the same as “empty tuple” and is the same as “unit”. (the last name probably came from mathematics)

Let me give some examples:

(1i16, 2i32) has the type (i16, i32)
(1i16, 2i32, 3i64) has the type (i16, i32, i64)
(1i16,) has the type (i16,) (again, this ugly syntax is the least of evils)
() has the type ()

Note that the compiler never “confuses” a type with an expression. (It looks at the context to figure out)


#8

So, to get this right.
I simply have to learn all the special cases (primitives, unit) so I can write stuff like MyType<i64> etc. But the struct-wrapping generics are just MyType<MyOtherType>?


#9

Yep! The only real special cases for types that you have to learn are [T; N] for arrays of one element of a specific length, and (T, Y, Z) tuples which represent a fixed-order mixed-typed list, essentialy.

Tuples themselves can be any length, from (i32, i32) representing two integers to any number of elements of different types. The type signature of a tuple is just () surrounding each individual type represented. The only reason () itself is special is because it’s the type chosen to typically represent “nothing”. The unit type itself is just a tuple with no elements, just like (i32) is a tuple with one element and (i32, i32) is a tuple with two elements.