String::drain¶
Level: reference · for working programmers
One line: Removes a byte range and yields the removed characters as an iterator — the removal you can read on the way out.
Stable since 1.6.0.
drain(..) empties the string and hands you its characters; drain(0..3) removes the first three bytes and yields those. The string is left holding the rest, with everything after the range shifted down.
The removal happens whether or not you consume the iterator: Drain removes the range when it is dropped, so s.drain(..3); on its own line is a valid way to delete a prefix.
The range panics on the usual two conditions: out of range, or an endpoint inside a character.
The string is mutably borrowed for as long as the Drain lives, so collect or finish before touching s again. Compared with replace_range, drain is the one that lets you see what was removed; compared with split_off, it keeps the tail in place rather than handing it back as a new String.
Example¶
string_drain.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 taken: String = s.drain(0..6).collect();
println!("took {taken:?}, left {s:?}");
// Dropping the Drain removes the range even if nothing is consumed.
let mut t = String::from("prefix:value");
t.drain(..7);
println!("{t:?}");
// drain(..) empties the string but keeps the buffer.
let mut all = String::with_capacity(32);
all.push_str("abc");
let chars: Vec<char> = all.drain(..).collect();
println!("{chars:?} left {all:?} capacity {}", all.capacity());
// Byte offsets, with the usual boundary rule.
let mut wide = String::from("héllo");
println!("boundary at 2? {}", wide.is_char_boundary(2));
let head: String = wide.drain(..3).collect();
println!("{head:?} + {wide:?}");
}
Verified output of string_drain.rs — regenerated by tools/run_examples.py, never hand-typed.
took "hello ", left "world"
"value"
['a', 'b', 'c'] left "" capacity 32
boundary at 2? false
"hé" + "llo"
See also¶
String::replace_range— removing a range and substituting textString::split_off— getting the tail back as an ownedStringString::retain— removing by a rule rather than a rangeString::clear— emptying without reading what went
String::drain in the standard library ↗
Po polsku¶
drain usuwa zakres i pozwala przy tym zobaczyć, co zniknęło — czym różni się od replace_range i clear: zwraca iterator po usuwanych znakach, więc s.drain(0..6) zebrane do String daje "hello ", a w s zostaje "world". Najbardziej zaskakuje to, że usunięcie następuje niezależnie od tego, czy iterator skonsumujesz — Drain wycina zakres w chwili wypuszczenia zasobu, dlatego samo t.drain(..7); w osobnym wierszu jest poprawnym sposobem skasowania przedrostka i z "prefix:value" zostaje "value". Zakres liczony jest w bajtach i panikuje na tych samych dwóch warunkach co reszta rodziny: poza zakresem albo w środku znaku — stąd boundary at 2? false dla "héllo" i cięcie dopiero na trójce. Dopóki Drain żyje, String jest pożyczony mutowalnie, więc dokończ iterację, zanim znów sięgniesz po zmienną; a gdy usunięta treść w ogóle cię nie interesuje, czytelniejsze będą clear albo replace_range.
Szukaj po polsku: usuwanie zakresu z łańcucha znaków · iterator po usuwanych znakach · rust String drain · rust drain removes on drop