All formats

unrar (Rust crate)

LanguageRust (bundles C++ code)
LicenseMIT or Apache-2.0 for the crate; bundled UnRAR source under the UnRAR license (not OSI open source)
Repositorygithub.com/muja/unrar.rs
Documentationdocs.rs/unrar
Depends ona C++ compiler at build time (the unrar_sys crate compiles the UnRAR source)
Read / writeyes / no; RAR4 and RAR5, passwords, multi-volume

The unrar crate is a Rust wrapper around RARLAB’s UnRAR library. It compiles the library from source during cargo build, so there is no system library to install.

The API follows the C library: entries are processed in order, front to back, with no random access. It uses the typestate pattern to enforce the order of calls: after read_header you hold an archive “before file” and must call skip, read, extract, extract_to or extract_with_base to get back to “before header”. Listing mode (open_for_listing) is an Iterator of headers.

Installation#

Shell
cargo add unrar

Basic usage: list and extract a RAR file#

Rust
use unrar::Archive;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut archive = Archive::new("archive.rar").open_for_processing()?;
    while let Some(header) = archive.read_header()? {
        let entry = header.entry();
        println!("{} ({} bytes)", entry.filename.display(), entry.unpacked_size);
        archive = if entry.is_file() {
            header.extract_with_base("output")? // keeps the folder structure under output/
        } else {
            header.skip()?
        };
    }
    Ok(())
}

extract() extracts relative to the current directory; extract_to(path) writes the entry to exactly that file path.

How to list files in a RAR#

Rust
use unrar::Archive;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    for entry in Archive::new("archive.rar").open_for_listing()? {
        let entry = entry?;
        println!(
            "{}{} {} bytes, encrypted: {}",
            entry.filename.display(),
            if entry.is_directory() { "/" } else { "" },
            entry.unpacked_size,
            entry.is_encrypted()
        );
    }
    Ok(())
}

A file split across volumes appears once with open_for_listing; open_for_listing_split returns one header per volume part.

How to read a file from a RAR into memory#

Rust
use std::path::Path;
use unrar::{Archive, UnrarResult};

fn read_entry(archive: &str, name: &str) -> UnrarResult<Option<Vec<u8>>> {
    let mut archive = Archive::new(archive).open_for_processing()?;
    while let Some(header) = archive.read_header()? {
        if header.entry().filename == Path::new(name) {
            let (data, _rest) = header.read()?;
            return Ok(Some(data));
        }
        archive = header.skip()?;
    }
    Ok(None)
}

read decompresses the whole entry into a Vec<u8>; use extract_to for large files.

How to open a password-protected RAR#

Rust
use unrar::error::Code;
use unrar::Archive;

fn main() {
    let result = Archive::with_password("secret.rar", "password")
        .open_for_processing()
        .and_then(|mut archive| {
            while let Some(header) = archive.read_header()? {
                archive = header.extract_with_base("output")?;
            }
            Ok(())
        });
    match result {
        Ok(()) => println!("done"),
        Err(e) if e.code == Code::BadPassword => eprintln!("wrong password"),
        Err(e) if e.code == Code::MissingPassword => eprintln!("password required"),
        Err(e) => eprintln!("error: {e}"),
    }
}

RAR4 archives store no password check value, so a wrong password shows up as Code::BadData (CRC error) during extraction. RAR5 archives report Code::BadPassword.

How to extract a multi-volume RAR#

Open the first volume; the library opens the next ones by name. Archive::first_part finds the first volume from any part name (it only looks at the string, not the disk):

Rust
use unrar::Archive;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let part = Archive::new("movie.part3.rar");
    let first = if part.is_multipart() { part.first_part() } else { "movie.part3.rar".into() };
    let mut archive = Archive::new(&first).open_for_processing()?;
    while let Some(header) = archive.read_header()? {
        archive = header.extract_with_base("output")?;
    }
    Ok(())
}

Tips#