A slice is a reference to a contiguous sequence of elements, written &[T] for a shared slice or &mut [T] for a mutable slice. Slices always borrow data and consist of a pointer plus a length, without owning the underlying storage. They are constructed with range syntax: [..] takes the whole sequence, [a..] from index a through the end, [..b] from the start to b exclusive, and [a..b] the elements from a to b exclusive. Because of unsized coercion, a function accepting &[T] can be called with a &Vec<T>, an array reference, or any other borrowing form that points to a contiguous run of Ts.
The relationship between String and &str is one of the most common applications of these ideas. A String is an owned, growable, heap-allocated UTF-8 buffer, while a &str is an immutable borrowed view into UTF-8 data — there is no way to mutate through a &str, so growing or modifying the text requires a String (or a &mut str). Creating a String from a literal can be done with String::from("hello") or "hello".to_string(), and extracting a borrowed view is as simple as &s (via deref coercion), &s[..], or the explicit s.as_str().
The reason s.len() works whether s is a String or a &str lies in the interaction between Deref and method resolution. The Deref trait, with its Deref::deref method, allows the compiler to dereference through a value with * and to coerce references automatically — a &String coerces to a &str because String implements Deref<Target = str>. This is called deref coercion, and it is what makes function arguments flexible: passing a &Vec<T> where a &[T] is expected works seamlessly. Auto-deref takes this further during method lookup, repeatedly dereferencing the receiver until a method is found, so chained methods on smart pointers often Just Work. The same principle applies to Box<T>, a smart pointer that owns a heap-allocated T and is dropped (along with the inner value) when it goes out of scope.