Strings and Text Processing
lines()
The .lines() method on str returns a std::str::Lines<'_> iterator that yields each line of text as a &str slice, stripping both Unix (\n) and Windows (\r\n) line endings automatically.
Signature and Line-Ending Rules
pub fn lines(&self) -> std::str::Lines<'_>
// where Lines<'_>: Iterator<Item = &str> + DoubleEndedIterator + FusedIterator
str::lines() follows three rules:
- Strips
\nand\r\n: Neither\nnor a preceding\r(in\r\n) is included in the yielded&strslices. - Treats a final trailing newline as a terminator:
"a\nb\n".lines()yields["a", "b"](2 lines), not["a", "b", ""]. - Preserves interior blank lines:
"a\n\nb".lines()yields["a", "", "b"](3 lines).
Input: "first\nsecond\r\n\nfourth\n"
│
▼ .lines()
Yields:
[0] "first"
[1] "second" (\r\n stripped)
[2] "" (blank line preserved)
[3] "fourth" (final trailing \n does not produce a 5th empty item)
fn main() {
let document = "first\nsecond\r\n\nfourth\n";
for (line_num, line) in document.lines().enumerate() {
println!("Line {}: {:?}", line_num + 1, line);
}
}
Line 1: "first"
Line 2: "second"
Line 3: ""
Line 4: "fourth"
s.lines() vs. s.split('\n')
| Aspect | s.lines() |
s.split('\n') |
|---|---|---|
Strips \r in \r\n? |
Yes ("a\r\n" yields "a") |
No ("a\r\n" yields "a\r") |
Final trailing \n ("a\n") |
Yields ["a"] (1 item) |
Yields ["a", ""] (2 items) |
Empty string "" |
Yields [] (0 items) |
Yields [""] (1 empty item) |
Always prefer .lines() over .split('\n') when processing multi-line text so that files saved with CRLF (\r\n) or trailing newlines do not leave stray \r characters or phantom empty lines at the end.