If I understand correctly there is no possibility for a variable within the code to own a literal string (one gets &'static str) or to de-reference a String with *myString (although one can get a slice.) So the ownership is only of the reference / slice, or of a String.
(I'm aware one can also do &*myString and other variants.)
Error returned is something about Sized trait not being implemented for strings, but it seems odd to me since in many cases it's quite clear that the size of a string is fixed.
I am a newcomer to Rust so if you could explain this not overly technical language that would be useful.
Literal strings are embedded in the compiled executable -- there is no owner variable. So they can only be accessed by reference. You could consider the executable to be the owner.
I'm not sure what you mean. If a variable is of type String, that variable is the owner. You can always chose to mutate that variable simply by adding mut to the variable binding (the let).
I'm not even sure what do you mean by that, sorry. Strings are unsized by their very nature. And if you would take language that tries to pretend that strings are “simple” you find insane amount of complexity, garbage collector – complete with bugs that survive for decades, or memory leaks (that are not really leaks) and other nastiness…
Because Rust doesn't believe in magic after compilation and to pretend that strings (that don't have fixed upper size limit) are “simple” variables one needs precisely that.
Because the type str is unsized. A specific str value has a fixed length of course, but the type does not have a fixed length.
You can't return variable-sized values because that's not the way stack allocation works. Local variables are allocated on the stack, and their size must be known at compile time. Not all languages work this way, but Rust does.
Because str doesn't have a fixed size and putting variables that don't have a fixed size is so hard that they even changed C11 to make that capability (that C99 added) optional.
Strings don't have a fixed size, it's as simple as that. That's the whole point of strings.
It's just difficult for the compiler to deal with and may be less performant, so it's not implemented by default. When you know the size at compile time you can easily preallocate the right number of bytes in the stack frame for the value, when you don't you have to manage the memory in more complicated ways.
In an alternative (or future) world we might have a str<N> type where N is the static length of the string, just like we have [T; N] arrays. In that world a string literal could have type &str<N>, similarly to array literals, and the *string_literal would become valid without relying on the unsized locals feature. However we don't have such type, so we're stuck with unsized strings for everything we do. Of course having such type would also come with disadvantages, like adding yet another string type (or actually type variant, since we might then have similar static-sized types for OsStr, Path and possibly others).
Thank you, I have been reading all the similar posts in this forum, and although I accept the answers here I think I will only understand this in the future.
But I think it would be nice if there was at least a section on this topic in the book since it seems to cause confusion to beginners.
The “baked into the executable” for of ownership, i.e. the ownership in static variables, is different… the implications of global variables is that any function (and call to any function) can access the variable, but no function owns it. So it’s inherently shared, which in Rust often means the same as “immutable”, and additionally its also inherently shared between threads, so it even requires Sync .
Is static variables which are the ones "baked into the executable" are sort of global in the sense that any function call access the same piece of data in this case the string, this in turn means that the static variable has to be immutable and it is shared as well.
This would also explain why they aren't neither in the heap nor in the stack.
(Apparently, statics are specifically in a read only segment of the executable. iiuc.)
However, of course my question only shifts to why did they need to be baked into the executable, and hence not shared although it may still be immutable.
For the Size part, I think I get now that the compiler does not see the contents of the file but only the type that's why str is not sized but str<N> would be. However, just like you said this exists for Arrays, one does not explicitly write the number of elements on the left as a type, so I guess I still find it confusing.
That may be related to you said above but may also not be
String literals, like other larger constants, e.g. constant arrays, have to be stored somewhere. And it is naturally to store them inside of the executable file, otherwise we would need a separate file. As the executable is loaded into RAM when we launch our application, the strings literals will then live somewhere in the memory space. So there is no real reason to copy them elsewhere, at least not when parts of our program needs only immutable shared access. Of course we can copy the literals from the initial memory location to other variables, to get local copies.
Static storage is a different kind of storage than heap and stack, yes.
The reason string literals are in static storage however is that they can't be stored on the heap, since the heap is not always available (e.g. when compiling for embedded targets or when using const functions), and while storing them on the stack would be possible (e.g. with that hypothetical str<N> I was talking about), it would be inconvenient due strings with different lengths having different types, and references to them living for less. Storing strings in static storage is just more convenient as it allows you to get long-lived 'static references with a common shared type, and it's also more efficient, especially for longer strings, because they are initialized only once at program startup rather than every time you have a string literal in your code.
You can have statics stored in writable memory too, they just need to be a static mut (nowadays discouraged though) or contain a UnsafeCell (generally indirectly through other types, like a Mutex or a LazyLock).
I'm not sure what you mean here, but if you want to store an array you have to write the number of items with [T; N]. If you're thinking of &[T] then this is exactly like the current &str, you can't dereference it to "own" the [T]. In fact str is essentially a [u8] except for the additional requirement that it needs to be valid UTF-8, and similarly String is just a wrapper over Vec<u8>; we just lack the equivalent of [u8; N] for strings.
My point there is that the number of items is inferred from what is on the right side of the assignment and the same could be done for strings. The only problem would be that utf-8 is a variable encoding etc.