Why declaring an image buffer without setting its color would result in an error?

I have this function

fn make_image(file_name: &str) {
    let mut imgbuf = image::ImageBuffer::new(640, 480);

    // for (x, y, pixel) in imgbuf.enumerate_pixels_mut() {
    //     let (r, g, b): (u8, u8, u8);
    //     r = if x < 320 {
    //         128
    //     } else {
    //         192
    //     };
    //     g = if y < 240 {
    //         128
    //     } else {
    //         192
    //     };
    //     b = 155;
    //     *pixel = image::Rgb([r, g, b]);
    // }

}

Note the commented lines.
If I uncomment them, the function would run just fine. I know it does not do anything, but at least it's compilable. However, when it commented the lines as shown, I get the error:

error[E0284]: type annotations needed for `image::ImageBuffer<P, std::vec::Vec<_>>`
 --> src\main.rs:5:22
  |
5 |     let mut imgbuf = image::ImageBuffer::new(640, 480);
  |         ----------   ^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `P`
  |         |
  |         consider giving `imgbuf` the explicit type `image::ImageBuffer<P, std::vec::Vec<_>>`, where the type parameter `P` is specified
  |
  = note: cannot satisfy `<_ as image::Pixel>::Subpixel == _`

Which is what I am trying to understand, as I am new to Rust.
Why declaring an image buffer without setting its color would result in an error?
Thank you.

ImageBuffer is a generic function. In this case, it's generic over a parameter P, which represents the type of pixel in the image buffer. Rust needs to know the type of P in order to know which actual function to call. If it can't figure out the type, it can't call the function, and thus the errors. "type annotations needed" means, please help me figure out the type.

1 Like

The problem is that on its own, by looking at the line

let mut imgbuf = image::ImageBuffer::new(640, 480);

the Rust compiler does not know the full type of your ImageBuffer. Note that ImageBuffer is a generic type

pub struct ImageBuffer<P: Pixel, Container> { /* fields omitted */ }

And while being a bit more restricted, the .new() method is generic, too:

impl<P: Pixel + 'static> ImageBuffer<P, Vec<P::Subpixel>>
where
    P::Subpixel: 'static,
{
    pub fn new(width: u32, height: u32) -> ImageBuffer<P, Vec<P::Subpixel>>;
}

Here, P can be any valid Pixel type, e.g. image::Rgb<u8> or image::Rgba<f32> or image::Luma<isize>, etc...

In the case where you don’t comment out the code, the fact that you’re using enumerate_pixels_mut, which yields an iterator over (u32, u32, &mut P), assign the third component of that iterator’s item to the pixel variable and finally do

*pixel = image::Rgb([r, g, b])

where r, g, and b are u8s allows the compiler to deduce that P must have been an image::Rgb<u8>.

Without that code, you can still make your program compiler, in various ways:

let mut imgbuf = image::ImageBuffer::<image::Rgb<u8>, _>::new(640, 480);
let mut imgbuf = <image::ImageBuffer<image::Rgb<u8>, _>>::new(640, 480);
let mut imgbuf: image::ImageBuffer<image::Rgb<u8>, _> = image::ImageBuffer::new(640, 480);
4 Likes