You can think of a String in Rust as holding three pieces of data - a pointer (to an allocated block of memory [on the heap] that holds a contiguous sequence of bytes - essentially a heap-allocated array of u8's), an integer that stores the capacity of the aforementioned block of memory (i.e. the size of the buffer), and an integer that stores the the size of the String (i.e. how much of the buffer is actually being used).
When you create a slice (a &str object) from a String, the slice will still be pointing to the data held by the String object. For all intents and purposes, the data comprising a slice is a const (using C-base parlance) pointer and an integer that indicates the immediate size of the slice (it supplies you NO information about the size of the underlying buffer). In the case of the original post ... the slice variable is referring to the data held by str (as an immutable borrow).
If you then look at the signature-line of the clear method for String objects ...
pub fn clear(&mut self)
You see that a method-call to clear involves a mutable reference of the calling-object. Thus, once you call the clear method, any access you may have had to the data via slice vanishes. The mutable reference from the method-call causes there to be a mutable-borrow. Your slice variable no-longer is borrowing the data. That's why the Rust compiler throws the error at you, as a result of your println! call.
Even in C or C++, your program would be a bad move, because you're trying to access data that you've just cleared. That could maybe be akin to accessing freed memory with a dangling pointer. These are among the many sort of memory errors that Rust's data-ownership/data-borrowing model attempts to prevent.
fn main() { let mut str: String = String::from("hello"); let slice: &str = &str[0..2]; println!("{}", slice); // prints he str.clear(); // slice is of no use after this line! str.insert_str(0,"world"); println!("{}", str); // prints world }
The code above compiles and runs without fault. However, it's important to realize that slice only is effectively borrowing the data through the first call to println!. After that, there is a transient mutable-borrow due to the call to clear and then ownership returns to str.
It's important to remember that you can have as many immutable references to an object as you like ... however, once you have a mutable borrow (a mutable reference), then all your immutable borrows are forfeit (you cannot use them again).
Of course, nothing stops you from creating new borrows!
fn main() { let mut str: String = String::from("hello"); let slice: &str = &str[0..2]; println!("{}", slice); // prints he str.clear(); // slice is of no use after this line! str.insert_str(0,"world"); println!("{}", str); // prints world let slice: &str = &str[0..2]; // new immutable borrow! println!("{}", slice); // prints wo }
Thus, as you can see, this motivates the whole discussion about the lifetimes of references (lifetimes of borrows) ... because, as you observed, they do not live indefinitely.
To address the question, why are &str and &String treated as references to the same value - the answer there is that they are NOT. They both CAN hold pointers to the same data-array (i.e. they can have a data-member in common, which is a pointer). However, in principle, the rest of their data is independent.
Also, you can define &str variables locally that are assigned primitive string-literals. These variables will exist purely locally on the stack. They provide one with means of doing many common string tasks with handy immutable data - without having to use any String object machinery. However, whenever you want that data to persist beyond the stack or you want to be able to mutate the data - then you enter the territory where String objects are particularly useful.
Anyhow, at the end of the day, &str objects serve as immutable, lightweight objects that are quite useful. Furthermore, since they are lightweight and flexible, they are a great way of handling immutable references to String objects too.
Stringandstr?&stris a slice, it can be a pointer to a area inside aString. They share memory so you can't modify theStringDereftrait allows implicit dereferencing. Since the standard library hasimpl Deref<str> for String, any*Tor&Twill call thederefmethod for you, aka "deref coercion". It isn't usually something that you will write in there yourself. I still think it is the criteria you are looking for, maybe that link will explain a little better than the docs.&str, from aPersonand while you are borrowing it, you cannot modify that person.