Copiar, renombrar y borrar archivos en Rust

Las tres operaciones básicas sobre archivos son una llamada cada una y están en std::fs, sin dependencias. Lo que conviene mirar con atención no son las llamadas sino qué hacen cuando el archivo no está donde se esperaba.

Las tres operaciones

use std::fs;
use std::path::Path;

fn main() -> std::io::Result<()> {
    let bytes = fs::copy("original.txt", "copia.txt")?;
    println!("copiados {bytes} bytes");

    fs::rename("copia.txt", "renombrado.txt")?;
    println!("renombrado.txt existe: {}", Path::new("renombrado.txt").exists());

    fs::remove_file("renombrado.txt")?;
    println!("tras borrarlo existe: {}", Path::new("renombrado.txt").exists());

    match fs::remove_file("no_estaba.txt") {
        Ok(()) => println!("borrado"),
        Err(e) if e.kind() == std::io::ErrorKind::NotFound => println!("no existía, nada que hacer"),
        Err(e) => return Err(e),
    }
    Ok(())
}
salidacopiados 19 bytes
renombrado.txt existe: true
tras borrarlo existe: false
no existía, nada que hacer

fs::copy devuelve cuántos bytes copió, dato que suele ignorarse pero sirve para comprobar que el archivo no estaba vacío. fs::rename hace doble función: cambia el nombre y, si el destino está en otra carpeta del mismo sistema de archivos, mueve.

Las tres sobrescriben sin preguntar. Si copia.txt ya existe, fs::copy lo reemplaza; si el destino de rename existe, también. No hay confirmación ni error. Cuando eso importe, hay que comprobarlo antes con Path::exists(), asumiendo que entre la comprobación y la operación otro proceso podría crear el archivo. Para una exclusividad real hay que abrir con OpenOptions::new().create_new(true), que falla si ya existe.

Distinguir «no estaba» de «no se pudo»

El último bloque del ejemplo es el patrón que conviene adoptar. Que un archivo no exista suele ser una situación prevista, mientras que un fallo de permisos es un problema de verdad, y tratarlos igual lleva a programas que ocultan errores reales o que fallan por nada.

La guarda if e.kind() == ErrorKind::NotFound separa los dos casos. Es la misma técnica de la lección de pattern matching, aplicada al tipo de error en lugar de a un valor.

Las operaciones de archivos y carpetas

Qué quieres Llamada
Copiar un archivo fs::copy(origen, destino)
Renombrar o mover fs::rename(antes, despues)
Borrar un archivo fs::remove_file(ruta)
Borrar una carpeta vacía fs::remove_dir(ruta)
Borrar una carpeta con contenido fs::remove_dir_all(ruta)
Comprobar si existe Path::new(r).exists()
Crear solo si no existe OpenOptions::new().create_new(true)
Cuidado con remove_dir_all. Borra la carpeta y todo lo que contenga, sin papelera y sin confirmación. Si la ruta viene de un argumento o de un archivo de configuración, conviene validarla antes: un error de cálculo en esa cadena borra más de lo previsto.

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: el módulo std::fs.