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:

  1. Strips \n and \r\n: Neither \n nor a preceding \r (in \r\n) is included in the yielded &str slices.
  2. Treats a final trailing newline as a terminator: "a\nb\n".lines() yields ["a", "b"] (2 lines), not ["a", "b", ""].
  3. 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.