¿Cuál es la forma de facto de leer y escribir archivos en Rust 1.x?

Con Rust siendo comparativamente nuevo, he visto demasiadas formas de leer y escribir archivos. Muchos son fragmentos extremadamente desordenados que alguien ideó para su blog, y el 99% de los ejemplos que he encontrado (incluso en Stack Overflow) provienen de versiones inestables que ya no funcionan. Ahora que Rust está estable, ¿qué es un fragmento simple, legible y sin pánico para leer o escribir archivos?

Esto es lo más cerca que he estado de algo que funciona en términos de leer un archivo de texto, pero todavía no está comstackndo, aunque estoy bastante seguro de haber incluido todo lo que debería tener. Esto se basa en un fragmento que encontré en Google+ de todos los lugares, y lo único que he cambiado es que el antiguo BufferedReader ahora es solo BufReader :

 use std::fs::File; use std::io::BufReader; use std::path::Path; fn main() { let path = Path::new("./textfile"); let mut file = BufReader::new(File::open(&path)); for line in file.lines() { println!("{}", line); } } 

El comstackdor se queja:

 error: the trait bound `std::result::Result: std::io::Read` is not satisfied [--explain E0277] --> src/main.rs:7:20 |> 7 |> let mut file = BufReader::new(File::open(&path)); |> ^^^^^^^^^^^^^^ note: required by `std::io::BufReader::new` error: no method named `lines` found for type `std::io::BufReader<std::result::Result>` in the current scope --> src/main.rs:8:22 |> 8 |> for line in file.lines() { |> ^^^^^ 

Para resumir, lo que estoy buscando es:

  • brevedad
  • legibilidad
  • cubre todos los errores posibles
  • no entra en pánico

Ninguna de las funciones que muestro aquí entra en pánico por sí sola, pero estoy usando expect porque no sé qué tipo de manejo de errores se adaptará mejor a su aplicación. Lea el capítulo sobre el manejo de errores del Rust Programming Language para comprender cómo manejar adecuadamente las fallas en su propio progtwig.

Rust 1.26 y más

Si no desea preocuparse por los detalles subyacentes, hay funciones de una sola línea para leer y escribir.

Leer un archivo en una String

 use std::fs; fn main() { let data = fs::read_to_string("/etc/hosts").expect("Unable to read file"); println!("{}", data); } 

Lea un archivo como un Vec

 use std::fs; fn main() { let data = fs::read("/etc/hosts").expect("Unable to read file"); println!("{}", data.len()); } 

Escribir un archivo

 use std::fs; fn main() { let data = "Some data!"; fs::write("/tmp/foo", data).expect("Unable to write file"); } 

Rust 1.0 y en adelante

Estos formularios son un poco más detallados que las funciones de una línea que le asignan un String o un Vec , pero son más potentes en cuanto a que puede reutilizar los datos asignados o anexarlos a un objeto existente.

Leyendo datos

Leer un archivo requiere dos piezas principales: File y Read .

Leer un archivo en una String

 use std::fs::File; use std::io::Read; fn main() { let mut data = String::new(); let mut f = File::open("/etc/hosts").expect("Unable to open file"); f.read_to_string(&mut data).expect("Unable to read string"); println!("{}", data); } 

Lea un archivo como un Vec

 use std::fs::File; use std::io::Read; fn main() { let mut data = Vec::new(); let mut f = File::open("/etc/hosts").expect("Unable to open file"); f.read_to_end(&mut data).expect("Unable to read data"); println!("{}", data.len()); } 

Escribir un archivo

Escribir un archivo es similar, excepto que usamos el rasgo de Write y siempre escribimos bytes. Puede convertir String / &str en bytes con as_bytes :

 use std::fs::File; use std::io::Write; fn main() { let data = "Some data!"; let mut f = File::create("/tmp/foo").expect("Unable to create file"); f.write_all(data.as_bytes()).expect("Unable to write data"); } 

Buffer E / S

Me sentí un poco empujado por la comunidad para usar BufReader y BufWriter lugar de leer directamente desde un archivo

Un lector de búfer (o escritor) utiliza un búfer para reducir el número de solicitudes de E / S. Por ejemplo, es mucho más eficiente acceder al disco una vez para leer 256 bytes en lugar de acceder al disco 256 veces.

Dicho esto, no creo que un lector / escritor en búfer sea útil al leer todo el archivo. read_to_end parece copiar datos en trozos algo grandes, por lo que la transferencia ya puede combinarse de forma natural en menos solicitudes de E / S.

Aquí hay un ejemplo de usarlo para leer:

 use std::fs::File; use std::io::{BufReader, Read}; fn main() { let mut data = String::new(); let f = File::open("/etc/hosts").expect("Unable to open file"); let mut br = BufReader::new(f); br.read_to_string(&mut data).expect("Unable to read string"); println!("{}", data); } 

Y para escribir:

 use std::fs::File; use std::io::{BufWriter, Write}; fn main() { let data = "Some data!"; let f = File::create("/tmp/foo").expect("Unable to create file"); let mut f = BufWriter::new(f); f.write_all(data.as_bytes()).expect("Unable to write data"); } 

Un BufReader es más útil cuando quieres leer línea por línea:

 use std::fs::File; use std::io::{BufRead, BufReader}; fn main() { let f = File::open("/etc/hosts").expect("Unable to open file"); let f = BufReader::new(f); for line in f.lines() { let line = line.expect("Unable to read line"); println!("Line: {}", line); } }