Could collections hypothetically store keys and values inline?
CAD97:
It is a form of it, yes. When you exceed the allocated inline space, all of the map is transferred to the heap. The Rust
enumoverlaps theHeapandInlinecase, using an invalid value of one to mark when the other is in use so the size equals that of the larger variant.Because Rust is statically typed, types must have a fixed stack size.
HashMapis currently 6×usize. You'll be hard-pressed to fit more than one key/value pair in that space. If you have a use case where most of your maps only have one int-string entry... maybe you shouldn't be using the general purpose stdlibHashMap.
Yes, sure, it is a form of optimization for small maps specifically. But it is not a direct analog of the "small string optimization" implemented by C++ and compact_str. It is a different way of optimizing for small maps by putting them on the stack[1] and so while you could reasonably call it a "small map optimization" in a vacuum, it is not the same thing and not what @Vorpal was originally referring to.
The direct analog is specifically storing things in those 6 usize-shaped values, which as you point out, wouldn't be very useful, which was my point.
CAD97:
Because Rust is statically typed, types must have a fixed stack size.
Also, this is further besides the point, but static typing has little to do with requiring types to have a fixed size on the stack. The only major language I'm aware of opting out of fixed-sized types on the stack as a matter of policy is Swift, which is statically typed and uses alloca heavily in its implementation of dynamically-linked generic types and such (How Swift Achieved Dynamic Linking Where Rust Couldn't - Faultlore is a very cool article on the topic, for anyone unfamiliar).
I think it's reasonable that Rust defaults to the position that everything must have a static stack size,[2] but it's not a fundamental property of static typing, nor does dynamic typing make it easier.
It doesn't seem like a particularly good implementation either—if your hash map is 16 items you should probably be doing linear search over an array. It also includes a 48 byte overhead over storing key-value pairs in an array and doing linear search, which seems fairly significant if you're optimizing for the case of small maps. ↩︎
Though maybe having more support for dynamically-sized objects would be a benefit to the language. It would be nice to be able to place dynamically-sized objects on the stack and to be able to express things like
dyn<T: Trait> Vec<T>where all the elements have the same underlying type (and thus stride) and so you can store them contiguously, only storing their vtable pointer once with theVecand avoiding a bunch of unnecessary allocations, compared to something likeVec<Box<dyn T>>. ↩︎
Discussion in the ATmosphere