[Solved] Why can I cheat mutability when writing to a file?


#1

What’s the mutability requirement for writing to a file? I find it rather strange that i need a mut f to be able to write! to it, but I can use a non-mutable variable to create a linked writer and then write! to that one.

If I change |mut f| to |f| here, I get a compiler error:

File::create(static_dir.join(&filename))
    .map(|mut f| {
        write!(f, "{}", css).unwrap();
    })
    .unwrap();

But this works just fine:

File::create(static_dir.join(format!("{}.gz", filename)))
    .map(|f| {
        write!(f.gz_encode(Compression::Best), "{}", css).unwrap();
    })
    .unwrap();

(Both examples from this source file.)


#2

You can always move an immutable value into a mutable one.

File::create(static_dir.join(&filename))
    .map(|f| {
        let mut m = f; // moved!
        write!(m, "{}", css).unwrap();
    })
    .unwrap();

In your second example, gz_encode is taking the file by value, and then it owns it. The temporary it returns can be mutated by write!, but if you had saved that to a local, you’d have to make that binding mutable.

If you had done File::create(...).as_ref().map(|f| ...) then it would be a truly immutable &File.


#3

Ah, so that’s what’s going on. It even makes some sense. Thanks for pointing it out!

What has me confused is that the actual value in this case (an open file) is kind of a reference in itself, but since that is not a Rust reference but one handled by the underlying operating system I guess I can’t expect the Rust mutability rules hold for it. :slight_smile:

Hm. That doesn’t seem to make a difference, I can still move f to mutable and write to it.


#4

Ah, weird, there is impl<'a> Write for &'a File, so it doesn’t actually have to be mutable to be written.

If you have that f: &File and then let mut m = f; you haven’t actually moved the file, just copied the reference. The mutability in mut m: &File generally just means you could just reassign m to some other &File. But with Write::write(&mut self), it can also use &mut &File to actually make the call.

With this knowledge, you could also write your original like:

File::create(static_dir.join(&filename))
    .map(|f| {
        write!(&f, "{}", css).unwrap();
    })
    .unwrap();

#5

Thanks @cuviper, I think I’ve got it covered now!