11# ` read_lines `
22
3- ## Beginner friendly method
4- This method is NOT efficient. It's here for beginners
5- who can't understand the efficient method yet.
3+ ## A naive approach
64
7- ``` rust,no_run
8- use std::fs::File;
9- use std::io::{ self, BufRead, BufReader };
5+ This might be a reasonable first attempt for a beginner's first
6+ implementation for reading lines from a file.
107
11- fn read_lines(filename: String) -> io::Lines<BufReader<File>> {
12- // Open the file in read-only mode.
13- let file = File::open(filename).unwrap();
14- // Read the file line by line, and return an iterator of the lines of the file.
15- return io::BufReader::new(file).lines();
16- }
8+ ``` rust,norun
9+ use std::fs::read_to_string;
1710
18- fn main() {
19- // Stores the iterator of lines of the file in lines variable.
20- let lines = read_lines("./hosts".to_string());
21- // Iterate over the lines of the file, and in this case print them.
22- for line in lines {
23- println!("{}", line.unwrap());
11+ fn read_lines(filename: &str) -> Vec<String> {
12+ let mut result = Vec::new();
13+
14+ for line in read_to_string(filename).unwrap().lines() {
15+ result.push(line.to_string())
2416 }
17+
18+ result
2519}
2620```
2721
28- Running this program simply prints the lines individually.
29- ``` shell
30- $ echo -e " 127.0.0.1\n192.168.0.1\n" > hosts
31- $ rustc read_lines.rs && ./read_lines
32- 127.0.0.1
33- 192.168.0.1
22+ Since the method ` lines() ` returns an iterator over the lines in the file,
23+ we can also perform a map inline and collect the results, yielding a more
24+ concise and fluent expression.
25+
26+ ``` rust,norun
27+ use std::fs::read_to_string;
28+
29+ fn read_lines(filename: &str) -> Vec<String> {
30+ read_to_string(filename)
31+ .unwrap() // panic on possible file-reading errors
32+ .lines() // split the string into an iterator of string slices
33+ .map(String::from) // make each slice into a string
34+ .collect() // gather them together into a vector
35+ }
3436```
3537
36- ## Efficient method
37- The method ` lines() ` returns an iterator over the lines
38- of a file .
38+ Note that in both examples above, we must convert the ` &str ` reference
39+ returned from ` lines() ` to the owned type ` String ` , using ` .to_string() `
40+ and ` String::from ` respectively .
3941
40- ` File::open ` expects a generic, ` AsRef<Path> ` . That's what
41- ` read_lines() ` expects as input.
42+ ## A more efficient approach
43+
44+ Here we pass ownership of the open ` File ` to a ` BufReader ` struct. ` BufReader ` uses an internal
45+ buffer to reduce intermediate allocations.
46+
47+ We also update ` read_lines ` to return an iterator instead of allocating new
48+ ` String ` objects in memory for each line.
4249
4350``` rust,no_run
4451use std::fs::File;
4552use std::io::{self, BufRead};
4653use std::path::Path;
4754
4855fn main() {
49- // File hosts must exist in current path before this produces output
50- if let Ok(lines) = read_lines("./hosts") {
56+ // File hosts.txt must exist in the current path
57+ if let Ok(lines) = read_lines("./hosts.txt ") {
5158 // Consumes the iterator, returns an (Optional) String
5259 for line in lines {
5360 if let Ok(ip) = line {
@@ -68,11 +75,15 @@ where P: AsRef<Path>, {
6875
6976Running this program simply prints the lines individually.
7077``` shell
71- $ echo -e " 127.0.0.1\n192.168.0.1\n" > hosts
78+ $ echo -e " 127.0.0.1\n192.168.0.1\n" > hosts.txt
7279$ rustc read_lines.rs && ./read_lines
7380127.0.0.1
7481192.168.0.1
7582```
7683
77- This process is more efficient than creating a ` String ` in memory
78- especially working with larger files.
84+ (Note that since ` File::open ` expects a generic ` AsRef<Path> ` as argument, we define our
85+ generic ` read_lines() ` method with the same generic constraint, using the ` where ` keyword.)
86+
87+ This process is more efficient than creating a ` String ` in memory with all of the file's
88+ contents. This can especially cause performance issues when working with larger files.
89+
0 commit comments