Using that simple technique in the book can be used in many cases, but not in cases where the size of the value is large enough to cause stack overflow -- for those cases, follow the advice in the other topic you linked.
There are no need to have any justification for a bug.
You may ask “why such an obvious bug wasn't fixed”, and the answer here is, essentially: “it's not easy to fix on one hand, and if size of your data structure is large enough for the size of stack to become a problem, then overhead from use of Vec instead of array would, probably, be acceptable, on the other hand”.
And you may even create data structures directly on heap with unsafe code, if you really-really-really need them.
What “magic” are you talking about? Rust book says how to put data on heap, it doesn't say anything about not putting it on stack, first.
The "magic" I am talking about (and asking to point to in the source code), is the code that, after creating the data structure on stack, moves it to the heap.
If it did that, it would cause stack overflow for a large data structure or (more likely) array, because the data structure would first be created on the stack. Doing it that way is the cause of the problem. To solve the problem, the data structure needs to be created directly on the heap after allocating space for it there.
The vec! macro does this when using the syntax that specifies an array of values. How it does it exactly, I don't know, but it looks like the relevant code is here (see the 3rd case of the macro that uses compiler intrinsics):
Currently vec![val; len] uses from_elem which by default places clones into the heap directly one by one (pre-optimization). And it's also specialized for a few cases to e.g. just allocate zeroed memory or use memset.
There is a lack of magic -- no guarantee that the stack isn't used -- even when optimizations are enabled. box syntax was supposed to do that, but it wasn't actually guaranteed, so it was scrapped. box_new is what remains of that attempt. Another attempt at emplacement has yet to emerge. So when you don't get the stack usage, it's due to optimizations, not guaranteed magic.
You could use vec![CONST; LEN].into() instead of Box::new([CONST; LEN]) specifically. There's no guarantee that will continue to act like it does today AFAIK, but it seems unlikely that it would be changed to start using lots of stack.
Keeping in mind the Vec<T> to Box<[T]> transform is simply dropping the capacity field (in the case where len equals capacity, which I believe is guaranteed for a fresh vec![]?)
Edit: yep:
If len == capacity, (as is the case for the vec! macro), then a Vec can be converted to and from a Box<[T]> without reallocating or moving the elements.
You can't really do better given the types, but it's probably worth further noting that if you don't have an iterator with exact length (eg you're using filter or the like), collecting to a boxed slice will likely add another reallocation; that is, it's not a "go faster" button.
As was shown that's just the function that is provided by a compiler.
In general Rust tries to ensure that all “magic” only ever happens during compilation, not in runtime.
Type checking is magical (as in: complicated, convoluted and not fully specified), type inference is magical (is in: complicated, convoluted and not fully specified), borrow checking is magical (: complicated, convoluted and not fully specified), but everything that happens in rutime is not magical.
This is actually very practical approach: the one consistent thing that magic consistently does… in most books where it's involved… is inconsistency.
All that magic that Rust (and other languages) does have the same problem: when magic (like garabage collection) works – it's nice and cool, when it fails – it's… not so nice.
With all magic moved to compilation time Rust may ensure that in Runtime everything is predictable.
It doesn't work 100% reliably, even simple malloc/free pair may misbehave (people often complain about the fact that on glibc this leads to fragmentation and jemalloc or tcmalloc are common cures), removing all magic is, apparently, more or less impossible. But Rust tries to minimize it.
Thankyou. So does this line specifically macros.rs - source call the heap allocator? If yes, how? Could you please point that out in compiler source code?
I didn't know any of these source code details before writing this comment, it's all just vanilla code exploration.
These aren't details your typical Rust programmer ever cares about. Embedded programmers are more likely to be aware of global_allocator and friends. FFI programmers need to be aware to not attempt to mix allocators. Even those groups usually don't care about how the compiler goes about lowering the source code to the system API (or beyond).
Technically it is not "guaranteed for a fresh vec![]". Vec::<T>::capacity is alwaysusize::MAX if T is a zero-sized type (ZST); thus something like vec![(); 4] has length of 4 but capacity of usize::MAX.