Having the "valid UTF-8" state being part of the type system means it needs to be checked only once when the instance is created (which can be compile-time for constants), and doesn't have to be re-checked later, even if the string is mutated. Unlike a generic bag of bytes, the pubic interface on string won't allow making it invalid UTF-8.
pub fn from_utf8(vec: Vec<u8, Global>) -> Result<String, FromUtf8Error>
which consumes the input Vec and returns it unmodified, if it's valid UTF-8,, or reports an error, if it's not. There are a number of related functions in this family. Such as pub fn from_utf8_lossy(v: &[u8]) -> Cow<'_, str>
which takes in a slice of bytes and checks if it's a UTF-8 string. If it is, it returns the original str. Otherwise it makes a copy with any errors replaced with the Unicode error character.Vec<u8> and array slices such as &[u8] are primitive containers - they can store any sequence of u8 values. String is more like an object with access methods.
If you know you have valid UTF-8, you can safely skip bounds checks when decoding a codepoint that spans multiple bytes.
str/String and CStr/CString are defined as UTF-8, though.
This is unfortunately misleading and a common misconception about OsStr. The documentation now explains:
> OsStr losslessly represents a borrowed reference to a platform string. However, this representation is not necessarily in a form native to the platform
What this means is that valid sequences of Unicode scalars are encoded as utf8 in OsStr on both Windows and Linux. The difference between OsStr and str is that the former can round-trip with the native encoding; that means that for Windows, there's a special way it encodes unpaired surrogates (wtf8) and on Linux it's actually just an arbitrary byte sequence.
This choice of representation means that on every platform: you can always borrow an OsStr from a str (as_ref) and you can sometimes borrow a str from an OsStr (to_str). This cross-borrowing wouldn't be possible if OsStr were UCS-2 on Windows.
In fact all four standard library types that are ToOwned without invoking Clone are more or less strings (str, CStr, OsStr, Path)
Actually now that I think harder about this, WTF did I think was really going on here before. If we clone the slice to make an array, where does the array go? We don't know how big that array is, so we can't put it on the stack.
Yeah, that was crazy. Thanks for pointing it out.