"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?
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.
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).
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.
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?
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.
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.