The difference between the "String" data type and the String "slice" data type (what I sometimes call an "amperstring") is one of the hardest things for newbie Rustaceans to comprehend.
let my_name = "Kent";
In most programming languages, the variable above, "my_name", would be a String variable, holding the string "Kent". In Rust, it's a String "slice" ("amperstring" in my own head). The above uses an implied way of writing:
let my_name: &str = "Kent"
If we wanted it to be an actual String instead of a slice variable, we could write it in either of these two ways:
let my_name: String = String::from("Kent"); let my_name: String = "Kent".to_string();
The actual text string "Kent" is written in a text-based, human-readable source-code file, that is read by the Cargo compiler, and stored in its internal memory storage as a String, but by the time it's presented to the rest of the program, just before it gets passed to the variable on the left side of the equals sign, only as a slice, not a String. That's why we have to use one of the two above conversion methods to get a String value into our variable.
Imagine the compiler's role in this as a packer in the packing department of a shipping retailer (Amazon, FedEX, etc). When the compiler ("packer") reads for the first time the four letters in double-quotation marks, "Kent", the compiler places each character of that String, the "K", and then the "e", and then the "n" and "t", into in its own cardboard box. Then the compiler goes to its shelves full of packing boxes, to the section that has boxes designed to hold Strings, and taking one, puts these four boxes into that box designed to hold a String. There are other types of boxes designed to hold other types of data, such as i32, or u8, etc. This String-box is then put in a storage bin, with a streaming video camera pointed at its contents, and the storage bin is locked, with the compiler keeping the key.
This first String-box is known only to the compiler; we don't have a name assigned to it.
When we, the programmer, need a variable pointing to that String, we can use a "let" statement, as above. If we ask for a slice, the compiler goes to its shelves of packing boxes, and gets one designed for slices, and binds the variable name we provide ("my_name" in this case) to that box. The slice-type box has a portable video screen built into it, which is "tuned" to the streaming-video content of the actual un-named String-box that the compiler has in its storage bin. So now we have a video-stream-recieving box to carry around that we can look at whenever we want, to see the value stored in the compiler's storage bin. We can't change that value; we can only look at it. We can request other variables also, so we might have three or ten different video-screens, all named with ("bound to") different variable names, all watching the same original String-box.
But if we ask for a String instead of a slice, the compiler takes out the key to the storage bin, binds our variable name to that key, and then gives us the key, so that we have direct access to the original String-box. We can make changes to this box, such as replacing one of the inner letter-holding boxes to make the String-box say "Tent", etc, and those changes will be reflected to any slice-style video-viewers that might be "out there" and tuned to this String-box.
This is not a perfect analogy, but it should demonstrate the basic difference between a String variable and a slice variable. The String variable is an actual "cardboard packing" box that we "own" and which we can access and manipulate/change, while the slice variable is more akin to a look-but-don't-touch video-stream "picture" of a compiler-owned, hidden-away String variable.