What's the right way to factor out bits of file writing?

Here is a tiny main.rs:

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

fn main() {
    if let Err(err) = output() {
        eprintln!("{}", err);
    }
}

fn output() -> io::Result<()> {
    let file = File::create("b.txt")?;
    let mut file = BufWriter::new(file);
    write!(file, "Title\n")?;
    // Is this the right way to factor out bits of file writing?
    let _ = body(&mut file)?;
    write!(file, "Footer\n")?;
    Ok(())
}

// Is there a nicer type I can use?
fn body(file: &mut io::BufWriter<std::fs::File>) -> io::Result<()> {
    write!(file, "Body\n")?;
    Ok(())
}

What I'm trying to do is factor writing parts of a file into helper functions and have any errors propogate up. Is this the right/best approach?

1 Like

seems fine to me. the signature could be

fn body(file: &mut BufWriter<File>) -> io::Result<()> {

since youve already used them, and is a bit nicer.

1 Like

Thanks, I've change to that and it works fine!

1 Like

You could also use a generic type:

fn body<T: Write>(file: &mut T) -> io::Result<()> {

With that, your function can use any type that implement Write. For example, if you want to use stdout instead of a file, you don't have to modify body.

Minor nit but usually you don’t require the caller to pass you a reference - you take the generic by value and then callers can decide if they want to pass you a reference or a value: fn body<W: Write>(mut w: W) -> ...

Only think I can think of is this bit: it should be alright just to use body(&mut file)?;. let _ = is only needed if you're discarding some result that you explicitly don't care about - but this expression returns (), which is already nothing.

Besides that, it looks good!

Thanks!