Skip to content

String::split_off

String methods · Strings

Level: reference · for working programmers

One line: Splits at a byte offset, returning the tail as a new String and leaving the head in place — panics off a character boundary.

pub fn split_off(&mut self, at: usize) -> String

Stable since 1.16.0.

s.split_off(5) shortens s to 5 bytes and gives you everything from byte 5 onward as an owned String. Both halves are usable afterwards, which is the difference from split_at: that borrows two views, this transfers ownership of the tail.

The tail is a new allocation; the head keeps the original buffer and its capacity. So this is not free, and repeatedly splitting off is repeatedly allocating.

split_off(0) moves the whole content into the returned string and leaves the original empty — a way to take a String's contents out through a &mut.

The usual panics: out of range, or inside a character.

Example

string_split_off.rs in full — pasted here by tools/run_examples.py from the file CI compiles and runs.

fn main() {
    let mut s = String::from("hello world");
    let tail = s.split_off(5);
    println!("head {s:?} tail {tail:?}");

    // Both halves are owned, unlike split_at's two borrows.
    println!("{:?}", "hello world".split_at(5));

    // The head keeps the buffer; the tail is a new allocation.
    let mut roomy = String::with_capacity(64);
    roomy.push_str("hello world");
    let cut = roomy.split_off(5);
    println!("head capacity {} / tail capacity {}", roomy.capacity(), cut.capacity());

    // split_off(0) takes the contents out through a &mut.
    let mut source = String::from("everything");
    let taken = source.split_off(0);
    println!("taken {taken:?}, source {source:?}");

    // Byte offsets, with the usual boundary rule.
    let wide = String::from("héllo");
    println!("boundary at 2? {}", wide.is_char_boundary(2));
    let mut copy = wide.clone();
    println!("{:?}", copy.split_off(3));
}

Verified output of string_split_off.rs — regenerated by tools/run_examples.py, never hand-typed.

head "hello" tail " world"
("hello", " world")
head capacity 64 / tail capacity 6
taken "everything", source ""
boundary at 2? false
"llo"

See also

String::split_off in the standard library ↗

Po polsku

Różnica wobec split_at dotyczy tego, kto co posiada: split_at zwraca dwa wycinki (string slices) pożyczone z oryginału, a split_off przenosi własność ogona do nowego String, zostawiając głowę na miejscu — po s.split_off(5) obie połówki da się niezależnie modyfikować. Cenę widać w przykładzie: głowa zachowuje stary bufor razem z pojemnością 64, a ogon dostaje świeżą alokację o pojemności 6, więc rozcinanie w pętli oznacza alokowanie w pętli. split_off(0) opróżnia oryginał i oddaje całą zawartość, co jest standardowym sposobem na wyjęcie treści Stringa przez &mut, kiedy nie mamy go na własność. Argument liczy bajty, nie znaki: "héllo".split_off(3) daje "llo", a przesunięcie 2 wypada w środku dwubajtowego é i program panikuje.

Szukaj po polsku: przenoszenie własności łańcucha znaków · granica znaku UTF-8 · rust String split_off · rust split_off vs split_at