Crear y escribir un archivo de texto en Rust

Escribir un archivo de texto en Rust son tres líneas si solo quieres volcar una cadena, y poco más si necesitas ir escribiendo línea a línea o añadir al final de un archivo que ya existe. Todo lo de esta página es biblioteca estándar: no hace falta añadir ninguna dependencia.

La forma corta: fs::write

Si lo que tienes es una cadena entera y quieres dejarla en disco, hay una sola llamada que crea el archivo, escribe y lo cierra. Si el archivo ya existía, lo reemplaza por completo.

use std::fs;

fn main() -> std::io::Result<()> {
    fs::write("notas.txt", "La primera línea\nLa segunda línea\n")?;
    println!("archivo escrito");
    Ok(())
}
notas.txtLa primera línea
La segunda línea

Fíjate en dos detalles de la firma. El ? después de la llamada propaga el error si algo falla, por ejemplo si no hay permisos de escritura en esa carpeta. Y para que ese ? sea legal, main declara que devuelve std::io::Result<()> y termina con Ok(()). Es el mismo mecanismo que explicamos en la lección de manejo de errores.

Línea a línea: File y BufWriter

Cuando el contenido se va generando sobre la marcha, conviene abrir el archivo una vez e ir escribiendo. BufWriter acumula en memoria y vuelca de golpe, lo que evita una llamada al sistema por cada línea:

use std::fs::File;
use std::io::{BufWriter, Write};

fn main() -> std::io::Result<()> {
    let archivo = File::create("inventario.txt")?;
    let mut salida = BufWriter::new(archivo);

    let articulos = [("tornillos", 120), ("tuercas", 80), ("arandelas", 45)];
    for (nombre, cantidad) in articulos {
        writeln!(salida, "{nombre};{cantidad}")?;
    }

    salida.flush()?;
    println!("{} líneas escritas", articulos.len());
    Ok(())
}
inventario.txttornillos;120
tuercas;80
arandelas;45

writeln! funciona igual que println! pero recibe como primer argumento el destino. Y el flush() del final fuerza el volcado de lo que quede en el búfer: Rust también lo hace al destruir el BufWriter, pero hacerlo explícitamente permite detectar un error de escritura, que de la otra forma se perdería en silencio.

Añadir al final sin borrar lo anterior

File::create siempre vacía el archivo. Para conservar el contenido y escribir detrás, hay que abrirlo con opciones:

use std::fs::OpenOptions;
use std::io::Write;

fn main() -> std::io::Result<()> {
    let mut registro = OpenOptions::new()
        .create(true)
        .append(true)
        .open("registro.log")?;

    writeln!(registro, "arranque correcto")?;
    println!("línea añadida al final");
    Ok(())
}

create(true) lo crea si no existe y append(true) coloca el cursor al final. Ejecutando ese programa tres veces, el archivo acaba así:

registro.log tras tres ejecucionesarranque correcto
arranque correcto
arranque correcto

Cuál usar

Situación Qué usar
Tengo la cadena entera y quiero volcarla fs::write
Voy generando el contenido línea a línea File::create + BufWriter
Quiero añadir sin borrar lo anterior OpenOptions con append(true)
Son datos binarios, no texto write_all con un &[u8]
El error con el que todo el mundo tropieza. Si usas writeln! sobre un File y olvidas importar el trait, el programa no compila: sale error[E0599]: cannot write into `File` con la nota «must implement io::Write». No es que falte el método, es que vive en un trait que hay que traer al ámbito. Basta con añadir use std::io::Write;, cosa que el propio compilador sugiere en el mensaje. Es la mecánica de los traits que explicamos en la lección 8.

Todo el código de esta página se compiló y ejecutó con rustc 1.96.1 antes de publicarla; las salidas están copiadas de la ejecución real. Documentación oficial: std::fs::write, BufWriter y OpenOptions.