Returning Slices
A function can return a slice reference (&[T], &mut [T], or &str) only when the backing elements outlive the function call—either because they were borrowed from an input parameter or because they reside in 'static memory.
Lifetime Rules When Returning a Slice
A slice does not own its elements; it is a 16-byte (ptr, len) view into an existing buffer. Consequently, the Rust borrow checker enforces three rules when returning a slice from a function:
- Returning a subslice of a single input parameter: Lifetime elision automatically ties the output slice's lifetime to the input parameter (
fn trim_zeros(s: &[u8]) -> &[u8]). - Returning a subslice when multiple reference parameters exist: You must annotate which input parameter the returned slice borrows from (
fn select<'a>(a: &'a [i32], b: &[i32]) -> &'a [i32]). - Never return a slice of a local array,
Vec<T>, orString: Local variables are dropped when the function returns; return an ownedVec<T>orStringinstead.
Valid vs. Invalid Slice Return Sources:
Caller Buffer (s: &[u8]) ──> fn strip_header(s: &[u8]) -> &[u8] (VALID: borrows from caller)
Static Memory ('static) ──> fn default_name() -> &'static str (VALID: lives forever)
Local Stack/Heap Buffer ──> fn bad() -> &[u8] (ERROR E0515: dropped at return)
Returning a Subslice of an Input Slice
fn strip_leading_zeros(bytes: &[u8]) -> &[u8] {
match bytes.iter().position(|&b| b != 0) {
Some(first_nonzero) => &bytes[first_nonzero..],
None => &[],
}
}
fn extract_between<'a>(text: &'a str, open: &str, close: &str) -> Option<&'a str> {
let start = text.find(open)? + open.len();
let rest = &text[start..];
let end = rest.find(close)?;
Some(&rest[..end])
}
fn main() {
let raw = [0, 0, 0, 42, 0, 99];
println!("Stripped: {:?}", strip_leading_zeros(&raw));
let tag = String::from("<title>Rust 2024</title>");
let open = String::from("<title>");
let close = String::from("</title>");
let content = extract_between(&tag, &open, &close);
// `open` and `close` can be dropped immediately because `content` only borrows `tag`:
drop(open);
drop(close);
println!("Extracted: {content:?}");
}
Stripped: [42, 0, 99]
Extracted: Some("Rust 2024")
Returning Mutable Subslices (&mut [T])
A function that accepts &mut [T] can return a narrowed &mut [T] or even a tuple of two disjoint mutable subslices ((&mut [T], &mut [T])) using .split_at_mut(mid):
fn payload_mut(packet: &mut [u8]) -> Option<&mut [u8]> {
// Skip a 4-byte header and return a mutable view of the remaining payload
packet.get_mut(4..)
}
fn main() {
let mut frame = [0x01, 0x02, 0x00, 0x04, 10, 20, 30, 40];
if let Some(body) = payload_mut(&mut frame) {
body.reverse();
}
println!("Frame after reversing payload: {frame:?}");
}
Frame after reversing payload: [1, 2, 0, 4, 40, 30, 20, 10]
Why Returning a Slice to a Local Variable Fails (E0515)
Creating a Vec<T>, String, or stack array [T; N] inside a function and attempting to return &local[..] fails to compile because the backing memory is deallocated when the function exits:
// ❌ does not compile
fn build_greeting(name: &str) -> &str {
let formatted = format!("Hello, {name}!");
&formatted[..]
}
fn main() {}
error[E0515]: cannot return value referencing local variable `formatted`
--> src/main.rs:4:5
|
4 | &formatted[..]
| ^---------^^^^
| ||
| |`formatted` is borrowed here
| returns a value referencing data owned by the current function
When a function constructs new sequence data rather than slicing an input buffer or returning a 'static literal, return the owned container (String, Vec<T>, or Box<[T]>) or Cow<'a, str> / Cow<'a, [T]>.