I want my Vec<u8> back from png::Writer

Hi all.

I'm using the png crate to write an image in png format, but I'd like to have the whole png playload in memory instead of in a regular file.

Starting from the provided example, I essentially replaced the File with a Vec<u8>:

use png;
use std::io::BufWriter;

fn main() {
    let destination = Vec::<u8>::new();
    let w = BufWriter::new(destination);

    let mut encoder = png::Encoder::new(w, 2, 1);
    encoder.set_color(png::ColorType::Rgba);
    encoder.set_depth(png::BitDepth::Eight);

    let mut writer = encoder.write_header().unwrap();

    let data = [255, 0, 0, 255, 0, 0, 0, 255]; 
    writer.write_image_data(&data).unwrap();
}

My destination vector gets "seized" by BufWriter, png::Encoder and finally by png::Writer . But I cannot find in the png::Writer documentation, and even in the rest of the crate docs how to access the png image payload stored in destination.

I want my vector back :frowning:

What I'm missing here? Is this a deliberate decision by the crate authors or they just didn't think about my use case?

I get this inaccessibility makes sense in the typical case of a regular file, but I think I'm entitled to access the data I need. I'm expecting something like the BufWriter::into_inner method.

Thanks for your support!

If W: Write, then &mut W: Write, too.

Just pass &mut destination to BufWriter::new().


By the way, use png; is useless. Dependent crates' top-level namespace is available without an explicit use.

2 Likes

I really didn't expect this.

Thanks a lot!

It's documented. In general, there are blanket impls for &T and/or &mut T (and other simple wrapper types such as Box and Mutex) for the most important traits whenever it makes sense.

1 Like

These blanket implementations aren’t always the easiest thing to find, especially if you haven’t yet learned to look for them. Another trait famous for having such an implementation is Iterator. Both Iterator and Write also do somewhat advertise the existence of these impls by offering by_ref convenience methods: Iterator::by_ref Write::by_ref.

In principal, any trait that doesn’t have any fn …(self) methods but only &self and &mut self ones – at least as far as the ones without default implementations are concerned (and which doesn’t otherwise use the Self type in function signature in ways that require ownership, can be implemented for &mut T based on an impl for T, and it’s often a good idea to do so if possible. (Also due to how orphan rules work, if you don’t write such an implementation for a trait, then adding it later may be a breaking change.) Similarly for &T if no methods require mutable access, either.

Hence it’s unsurprising that actually there’s quite a few traits that do the same (but – except for Read –not featuring a by_ref method). Probably because immutability is always a bit less surprising, no-one will give a second thought about various immutable-access-only traits having implementations for &T based on the one for T.

This would include Display and the other formatting traits, Ord and the other comparison and hashing traits, which all support &T and &mut T (incidentally Hasher, not a hashing trait, has an impl for &mut T, too).

The situation for operator traits is a bit complex… while many these traits take operands by-value anyways, the likes of Add features tons of implementations for various reference types, too. The operator overloading traits that do work with &mut self only, like AddAssign or IndexMut, don’t have a generic implementation for &mut T, but that’s probably because we want these operator overloading traits to be as custom-implementable for users as they like. FnOnce on the other hand famously has an impementation for &mut F, and Fn for &F and &mut F, and these are commonly very useful, if you need to share a or borrow a closure without consuming it, but then pass the result into some general x: impl FnOnce for example (though of course wrapping a closure with a new closure would’ve also always been possible to do such a call).

The traits AsRef/AsMut and Borrow/BorrowMut are infamous for having implementations for &T and &mut T that differ in whether or not any underlying implementation for T is delegated to. AsRef then goes on and makes the unfortunate design choice to not do the same delegation for other smart pointer types.

The trait Error has an implementation for &T. ToOwned doesn’t feature any implementation for &T, but such an implementation would be impossible due to how the implementation for Borrow is defined (which was a topic in the previous paragraph).

With this, I think I’ve made it through most of the list of standard library traits that are presented here, skipping the ones that use Self in an owned fashion. Similar considerations apply to Box<T> implementations, too, except that it even supports ownership, and is famously implemented for FnOnce, too, and probably for most traits discussed above; and maybe more? (I’m not looking into that right now.)


Edit, also worth mentioning, fmt::Write which also has an implementation for &mut T, but doesn’t feature a by_ref method.

2 Likes

May I ask you what is the purpose of these by_ref methods? Why not just pass around a &mut reference of the object?

It's convenience. If you want to reuse an iterator after calling a method that takes self by value, then it's nicer to write eg. iter.by_ref().collect() than (&mut iter).collect().

3 Likes