Skip to content

Commit 92faa26

Browse files
authored
Merge pull request #1679 from pringshia/read_lines
Improve the content for `read_lines`
2 parents af0998b + 9079c89 commit 92faa26

File tree

1 file changed

+45
-34
lines changed

1 file changed

+45
-34
lines changed

src/std_misc/file/read_lines.md

+45-34
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,60 @@
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
4451
use std::fs::File;
4552
use std::io::{self, BufRead};
4653
use std::path::Path;
4754
4855
fn 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

6976
Running 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
7380
127.0.0.1
7481
192.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

Comments
 (0)