That would introduce special cases into the type system, adding complexity. String is a library type, so you can take a reference to it like any other type. We'd have to move String into the language for that to happen, and currently, the language itself knows nothing about allocation, so then we'd have to put allocation into the language, which would then change our story on embedded significantly... and then it'd be one of the only types that you couldn't take a reference to for some reason, which would affect generic APIs, etc...
Vec's "special case" is [].
And it's not that String "has" a special case, it's that str is a special fundamental case of the language in the same way i8 or () is and String exists to make it easier to work with (otherwise you'd have to deal with Box<str> and efficiently working with that would require going back and forth to Vec, except you'd have removed Vec since it'd be a special case of [] and where do you store capacities at this point?)
> C++ needs string vs string_view because it has no borrow checker, but rust could (theoretically) implement this without the need for two different types.
C++ has char* which conceptually underlies both string and string_view. Rust shoves char* in the unsafe corner but need something to replace it, something which not only means "a bag of bytes" ([u8] does that just fine) but text as in "actual proper utf8-encoded unicode text". That's str.
> no other data type does?
You could argue that slices are a primitive type to arrays (also a primitive type) and vectors (a library type).
> Why does there need to be a distinct data type for a non-owned view into an object?
The difference between owned and non-owned types is fundamental; how would you propose distinguishing them if not as part of the type? Both concepts are part of the language, but like any language, you can use its fundamental bits to build better abstractions.
I absolutely don't think &str should silently allocate for converting to String. But I think &str should zero-allocation convert to &string (and bypass dealloc, too).