On-Ramp to Rust

the slice and the string types

In C, a chunk of contiguous data is usually described by two loose pieces you must keep in sync yourself: a pointer to the first element and a separate length (and for strings, a NUL terminator marking the end). Lose track of the length, or read past the terminator, and you are off the end of the array — the classic buffer overrun. Rust packages the pointer and the length together into one safe, bounds-checked thing: the slice. A slice, written &[T], is a borrowed view into a run of values of type T living somewhere else — it knows both where the data starts and how many elements there are, so the language can check every access against that length.

Strings come in two related types that beginners often mix up, and the distinction is exactly the ownership distinction you already know. String is an owned, growable, heap-allocated UTF-8 string — it owns its bytes and frees them when it drops, much like a value that owns a malloc'd buffer. &str (pronounced string slice) is a borrowed view into some existing UTF-8 text — it owns nothing, it is just a pointer plus a length pointing into bytes someone else owns. A string literal like "hello" in your source code is a &str pointing into the program's read-only data. The everyday pattern is to store and build text as a String, but to accept &str in function parameters, because a function that only needs to read text should borrow a view rather than demand ownership — and a &String can be lent as a &str automatically, so &str parameters accept both.

Why this matters: the slice is how Rust gives you C's efficiency — passing a view of data with no copying — while keeping the length attached so out-of-bounds access is caught (it panics safely rather than reading wild memory). And the String versus &str split is just ownership versus borrowing applied to text: own it when you need to keep or modify it, borrow a &str when you only need to read it. The common beginner stumble is reaching for String everywhere out of habit; the idiomatic move is to take &str arguments, which is both more flexible for callers and avoids needless copies.

fn shout(text: &str) -> String { // borrow to read, return an owned result text.to_uppercase() } let owned = String::from("hi"); // owns heap bytes let view: &str = &owned; // a borrowed slice into them println!("{}", shout(view)); // HI (literal "hi" is also a &str)

String owns its bytes; &str borrows a bounds-checked view. Prefer &str for read-only parameters.

A slice carries its length, so indexing is bounds-checked and an out-of-range index panics rather than reading wild memory. String versus &str is just owned versus borrowed applied to text — not two unrelated string classes.

Also called
slice&[T]String&str切片字串切片