I can observe thanks to std::any::type_name_of_val() that the documented std::ops::Range is actually a core::ops::range::Range, or that the std::vec::Vec is a alloc::vec::Vec.
My best guess is that
the doc is stable and gives the correct "public" type name
the run-time type reflects an internal structure that might evolve when refactoring the std, or match an internal implementation selected at run-time depending on the context (for instance, a smart Vec could be allocated on the stack for small values and on the heap otherwise)
Is it correct ?
However, I feel that the "core" module name is special. What is its relation to the std ?
Std is split into three public crates: core, alloc and std. Std forwards many declarations from core and alloc. You can build no-std crates that don't depend on std. This is useful in embedded, making your own OS or other places where you can't use the full standard library.
Rust’s standard library is divided into a few major parts because some platforms that Rust supports don’t have the capability to provide everything:
core is the smallest piece, and is always available. This contains primarily routines that are purely computational, which can be implemented on any system that can be considered a computer, including things like tiny embedded chips.
alloc then adds support for systems that have a heap with a dynamic allocator, and includes types like Box and Vec that help manage data stored in that heap
std further adds support for things that are generally provided by an operating system instead of raw hardware, like filesystem access, networking, and terminal I/O.
Also, because most code is written for a std-compatible environment, it includes type aliases for most things defined in the other two parts for convenience.
(There may be additional divisions these days; I don’t keep track all that closely)
The result of type_name or type_name_of_val is a &'static str, not a type. Rust currently doesn't have type_of!(..) or the like. Moreover,
This is intended for diagnostic use. The exact contents and format of the string returned are not specified, other than being a best-effort description of the type. For example, amongst the strings that type_name::<Option<String>>() might return are "Option<String>" and "std::option::Option<std::string::String>".
The returned string must not be considered to be a unique identifier of a type as multiple types may map to the same type name. Similarly, there is no guarantee that all parts of a type will appear in the returned string: for example, lifetime specifiers are currently not included. In addition, the output may change between versions of the compiler.
The current implementation uses the same infrastructure as compiler diagnostics and debuginfo, but this is not guaranteed.
If you want run-time type information, the closest thing in Rust is TypeId, usually used indirectly via dyn Any. It doesn't cover all types. Doing so dynamically is impossible in the general case, as lifetimes are compile-time only information, but types that differ by lifetime are different types, for example.
If you only care about "where is the type really", that's not truly stable either. Sometimes types move location and become re-exported, or become type aliases to the new location. For example many types have moved from std to alloc or core over time. Types can also gain defaulted generic parameters in a relatively backwards compatible way; for example the Allocator generic of Vec wasn't always present (and still isn't stable and there's a possibility it will be removed instead of stabilized, etc).
Yes, that was just an example of something that could justify that std::vec:: becomes alloc::vec (cleary on the heap), letting room for something else (even not available yet)
But I get it, now, it was not the good explanation.