The &str type is allocated on the stack directly due to its fixed size.
Like with String
, the pointer and length -- the actual things that make up the type -- are stored on the stack, but in the case of &str
, the pointed-to string data may live anywhere: if it came from a String
, the data lives where the String
owns it on the heap, while if it came from an array, it lives on the stack.
Roughly speaking, a &str
is represented by
struct StrRef {
data: *const u8,
len: usize,
}
So this is constructed from a String
by doing something like
let owned = String::from("hello");
let borrowed = StrRef {
data: owned.as_ptr(), // via Deref<Target = str>,
len: owned.len(),
};
More strictly speaking, str
(without the &
) is a dynamically sized type, which means that a reference to it (&str
) is wide -- it stores both a pointer to the data and some metadata, which in the case of str
and [T]
is a usize
representing the length.
This wide pointer ends up looking something like StrRef
above, but the exact representation of wide pointers is (currently) not a stable part of the language; str
and [T]
are treated specially by the compiler, as is dyn Trait
(where the metadata is a pointer to a vtable).