You are correct, there is no safety --- you can call the constructor std::string_view(const char, size_t) and stuff anything you want in there. The main reason string_view is nice is the implicit conversions you get from the other string types. Pass in a "const char*", it finds the terminating null for you. Pass in a std::string, no problem. No boilerplate.
But the real benefit is in providing it as a "vocabulary type". Once C++ code starts using string_view everywhere, you don't need to massage your char sequences this way and that [1] to pass it from the top of your application all the way down into the low-level libraries the application relies on. And you don't incur the overhead (both in lines of code and in copies) of all that massaging.
[1] Maybe your request handler has to convert from char* and size to a string to call your business logic, then your string has to convert from a string to a char pointer and and int length to call saveName. saveName might actually use std::copy under the hood to put the data into a chunk of memory, so it turns the pointer and length into two pointers.
- It encodes the invariant in a type (char * points to at least size bytes, no expectation of null termination, no ambiguous definition of an empty range)
- The size always travel with the pointer so no chance that it gets separated.
- syntactically simple conversion from string (or other string-like entities) to string view reduces clutter in the code.
I consider lack of null termination a plus.