0

I have this program:

use std::io::{self, Cursor, prelude::*};

fn file_op<R: Read>(mut reader: R) -> io::Result<()> {
    // Some file operation
    let mut buf = [0u8];
    reader.read(&mut buf)?;
    Ok(())
}

fn main() -> io::Result<()> {
    let mut f = Cursor::new(vec![0xffu8]);

    // Works    
    file_op(&mut f)?;
    file_op(&mut f)?;

    let x = &mut f;

    // Does not work
    file_op(x)?;
    file_op(x)?;

    Ok(())
}

I understand that the first portion (// Works) works because any &mut T where T: Read also implements Read (from impl<'a, R: Read + ?Sized> Read for &'a mut R in libstd).

However, I don't understand why the second portion (// Does not work) causes a use after move error. Isn't the R type parameter a &mut Cursor (which implements Read) in both cases? If I'm only using references, what is getting moved? The reference variable, x, itself?

error[E0382]: use of moved value: `x`
  --> src/main.rs:21:13
   |
20 |     file_op(x)?;
   |             - value moved here
21 |     file_op(x)?;
   |             ^ value used here after move
   |
   = note: move occurs because `x` has type `&mut std::io::Cursor<std::vec::Vec<u8>>`, which does not implement the `Copy` trait

Interestingly, dereferencing x and referencing it again also works:

file_op(&mut *x)?;
file_op(&mut *x)?;
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Andrew Gunnerson
  • 638
  • 1
  • 8
  • 17
  • 1
    I believe the last part of your question is answered by the answers of [Why does creating a mutable reference to a dereferenced mutable reference work?](https://stackoverflow.com/q/45095523/155423). – Shepmaster Oct 16 '18 at 02:04
  • 1
    I believe the rest of your question is answered by the answers of [Retaking ownership of a mutable reference passed to a function accepting a generic type](https://stackoverflow.com/q/48143889/155423). If you disagree, please [edit] your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Oct 16 '18 at 02:06
  • @Shepmaster Thank you! Yes, those two links answer my questions. – Andrew Gunnerson Oct 16 '18 at 03:41
  • A better design for your function is to mutably borrow the argument: `fn file_op(reader: &mut R) -> io::Result<()> {`. Then everything work without further ado. – starblue Oct 16 '18 at 07:43
  • Or with `impl Trait` in argument position: `fn file_op(reader: &mut impl Read) -> io::Result<()>` – Lukas Kalbertodt Oct 16 '18 at 09:55
  • I actually disagree with the two previous comments that taking a mutable reference is a *better* design. – Shepmaster Oct 16 '18 at 12:33
  • I actually did have the function take a mutable reference before, but changed it based on the recommendation in the Rust API guidelines: https://rust-lang-nursery.github.io/api-guidelines/interoperability.html#generic-readerwriter-functions-take-r-read-and-w-write-by-value-c-rw-value – Andrew Gunnerson Oct 16 '18 at 23:58

0 Answers0