Summarizing built-in types

Thanks for the feedback @quinedot!

"The Rust Programming Language" book, chapter 3, section "Compound Types" says "Rust has two primitive compound types: tuples and arrays." So I thought I should refer to all the other compound types as "non-primitive".

In the latest version of my diagram, "enum" and "union" are far apart.

I didn't include OsStr because I didn't know about it, but also because I decided to draw the line at anything related to FFI and unsafe things. Maybe someday I'll include those in a more advanced diagram.

If I include Path where does it belong?

I'm open to adding dyn, but don't know where to put it.

Are function pointers distinct enough from pointers to other types that I should include them as a unique type?

The latest version of the diagram is at https://mvolkmann.github.io/blog/assets/rust-types.png?v=1.0.3.

2 Likes

Are function pointers distinct enough from pointers to other types that I should include them as a unique type?

I think so; unlike &T or *const T, there is no corresponding T for fn() - the pointer itself is the simplest primitive.

I saw you have a function section, I would definitely include Fn, FnMut, and FnOnce along with function pointers. Although these are technically traits, you normally only see them implemented by closures, which are language primitives.

1 Like

If I include Path where does it belong?

Path and OsStr are fundamentally the same, so I wouldn't include one without the other. Path just has some convenience functions like .join().

I'm open to adding dyn, but don't know where to put it.

I think dyn is different in kind from the types you have already, I wouldn't include it.

also because I decided to draw the line at anything related to FFI and unsafe things.

Hmm, I'm confused then, because I saw you mentioned raw pointers.

1 Like

I guess anonymous function and closure types are the built-in types for this (and without indirection) but they have unique and anonymous types, so we can only include them as (anonymous).

2 Likes

An updated version of my Rust Types diagram incorporating recent suggestions is available at https://mvolkmann.github.io/blog/assets/rust-types.png?v=1.0.4.

2 Likes

You may be interested in the distinction between function items and function pointers, which you can read about here.

Also, the typically we call "shared mutation" for "interior mutability"? Though "shared mutation" may be readable.

1 Like

Currently I have the box "fn() function pointer". Are you saying that "fn()" is a "function item" which is distinct from a "function pointer", so those should appear in separate boxes in the diagram? IIUC the link you shared, a function item is a type whereas a function pointer is a pointer to a specific instance of a function. Is that correct?

I think I prefer the category "interior mutability" over "shared mutation", so I'll make that change.

A function item is a zero-sized type that corresponds to one single function, and each function has its own function item type. A function pointer is a type of size one pointer that can contain any function in the program. So for example given this function:

fn my_func() {
    println!("Hello!");
}

the corresponding function item type for this function would be similar to the following:

// no fields, so zero-sized
struct MyFuncFnItem {}

impl Fn() for MyFuncFnItem {
    fn call(&self, args: ()) -> () {
        println!("Hello!");
    }
}

On the other hand, a function pointer is a type shared between all functions, and the type is 8 bytes large.

3 Likes

I think I understand why I would use function pointers, but it's not clear to me why I would use a "function item" in code. Can you give me a simple example of when that is useful?

If you write the following code:

vec.iter().map(some_fn).collect();

Then the some_fn is a function item, not a function pointer. This helps the optimizer hard-code the address of the some_fn function in the generated assembly, instead of inserting a dynamic call to a function pointer.

4 Likes

There is a lot I like about the chart. A good "goto" to remind myself of the fundamentals when I get tangled up over-thinking something.

When designing my abstractions, I have found it useful to remind myself that literally everything can be represented by some combination of a product (describe using different qualities simultaneously, e.g., (hair color, age) to describe a person) and the sum type (can only be one value at a time e.g., gender to describe a person).

Perhaps for the data structures:

  • product type (many simultaneously) = compound type (functions when viewed as "functions as values" can also be represented here (input, output), but while true, perhaps not a useful, dominant choice),

  • sum type (can only be one of) = enum and union

The way I imagined it might be in terms of a small inset that compliments the current approach to organizing the types.