How to handle "use of moved value in previous iteration of loop" when writing to a BufWriter?

Viewed 302

I am encountering an error with the following code:

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

fn mandel_color(i: f32, mut writer: BufWriter<&File>) {
    let ir = 16.0 * (i % 15.0);
    let ig = 32.0 * (i % 7.0);
    let ib = 8.0*(i % 31.0);
    write!(&mut writer, "{} {} {}\n", ir, ig, ib).expect("unable to write to file.");
}

fn mandel_iter(cx: f32, cy: f32) -> f32 {
    let mut pair = (0.0, 0.0);
    let mut count = 0.0;
    while f32::sqrt(f32::powf(pair.0,2.0) + f32::powf(pair.1,2.0)) < 2.0 && count < 255.0 {
        pair = ((f32::powf(pair.0,2.0) - f32::powf(pair.1,2.0) + cx), (2.0*pair.0*pair.1 + cy));
        count += 1.0;
    }
    count
}

fn mandelbrot(width: i32, height: i32) {
    let file = File::create("img/image.ppm").expect("unable to create image.ppm");
    let mut writer = BufWriter::new(&file);
    write!(&mut writer, "P3\n{} {}\n255\n", width, height).expect("unable to write to file.");
    for x in 0..width {
        for j in 0..height {
            let x_ish = ((x as f32 - width as f32 * 11.0/15.0) / (width as f32 /3.0)) as f32;
            let y_ish = ((j as f32- height as f32 * 2.0) / (height as f32 * 3.0/10.0)) as f32;
            // how do i fix the error with writer here?
            mandel_color(mandel_iter(x_ish, y_ish), writer);
        }
    }
}

fn main() {
     const IMAGE_WIDTH: i32 = 256;
     const IMAGE_HEIGHT: i32 = 256;
     mandelbrot(IMAGE_HEIGHT, IMAGE_WIDTH);
}
error[E0382]: use of moved value: `writer`
  --> src/main.rs:32:53
   |
25 |      let mut writer = BufWriter::new(&file);
   |          ---------- move occurs because `writer` has type `BufWriter<&File>`, which does not implement the `Copy` trait
...
32 |             mandel_color(mandel_iter(x_ish, y_ish), writer);
   |                                                     ^^^^^^ value moved here, in previous iteration of loop

Link to playground

I tried setting up a struct for Writer with #[derive(Copy)] but BufWriter<&File> can't implement the Copy trait... so I was unsure of how I should handle this case...

How do I handle moved value in a loop? specifically this part:

mandel_color(mandel_iter(x_ish, y_ish), writer);
1 Answers

Your original code moves the value to mandel_color(), which means not only that the caller cannot continue using it, but also that mandel_color will own the writer and therefore close it once it writes a single triplet and writer goes out of scope. This transfer of ownership was certainly not intended.

You can prevent ownership transfer and fix the "moved value" error by not moving the value, in this case simply by having mandel_color accept a reference to the writer:

fn mandel_color(i: f32, writer: &mut BufWriter<&File>) {
    let ir = 16.0 * (i % 15.0);
    let ig = 32.0 * (i % 7.0);
    let ib = 8.0 * (i % 31.0);
    write!(writer, "{} {} {}\n", ir, ig, ib).expect("unable to write to file.");
}

with the call site adjusted to take the reference:

mandel_color(mandel_iter(x_ish, y_ish), &mut writer);

You can also make mandel_color more generic (without loss of performance) by making it work with any type that implements the Write trait. That way you don't have to spell out the exact type BufWriter<&File> and your code will work unchanged on outputs that write to a network socket, to the standard output, to memory, etc.:

fn mandel_color(i: f32, mut writer: impl Write) {
    let ir = 16.0 * (i % 15.0);
    let ig = 32.0 * (i % 7.0);
    let ib = 8.0 * (i % 31.0);
    write!(writer, "{} {} {}\n", ir, ig, ib).expect("unable to write to file.");
}

Playground

Related