Kannan Goundan
Kannan Goundan

Reputation: 5242

How to call serde_json::to_writer twice with the same mutable borrow?

I'm trying to write a function that calls serde_json::to_writer twice to write two things, but I can't figure out how.

Here's one attempt (playground link):

extern crate serde_json;

fn main() {
    let mut out = std::fs::File::create("out.txt").unwrap();
    write_stuff(&mut out);
}

fn write_stuff<W: ?Sized>(out: &mut W) -> Result<(), std::io::Error>
where
    W: std::io::Write,
{
    serde_json::to_writer(out, &1).unwrap();  // `out`: value moved here
    serde_json::to_writer(out, &2).unwrap();  // `out`: value used here after move
    Ok(())
}

Edit: Figured out a way to get things to compile, but is there an easier way (playground link):

extern crate serde_json;

fn main() {
    let mut out = std::fs::File::create("out.txt").unwrap();
    write_stuff(&mut out);
}

fn write_stuff<W: ?Sized>(out: &mut W)
where
    W: std::io::Write,
{
    write_wrapper(out);
    write_wrapper(out);
}

fn write_wrapper<W: ?Sized>(out: &mut W)
where
    W: std::io::Write,
{
    serde_json::to_writer(out, &1).unwrap();  
}

Upvotes: 6

Views: 808

Answers (1)

Sven Marnach
Sven Marnach

Reputation: 602355

The reason for this behaviour is somewhat subtle. When passing a shared reference as parameter to a function, Rust will simply copy the reference. The type &T is Copy for all T, since we are allowed to have any number of shared references at the same time.

Mutable references, on the other hand, are not Copy, since there can be only one of them at any given time. According to the usual Rust semantics for non-Copy types, this means that mutable references should be moved when passed as a parameter. So why does this code work?

fn foo(_: &mut i32) {}

fn main() {
    let mut i = 42;
    let r = &mut i;
    foo(r);
    foo(r);
}

The reason is that the compiler creates an implicit reborrow whenever it assigns to a variable that is explicitly declared as a mutable reference, so the function calls are translated to foo(&mut *r). This creates a new borrow that only lasts for the duration of the function call, and the original reference r becomes available again once the lifetime of the reborrow ends.

However, implicit reborrows are only generated for variables that are explicitly declared with a mutable reference type. If we change the definition of foo() above to

fn foo<T>(_: T) {}

the code will stop compiling. Now the parameter of foo() is not declared as a mutable reference anymore, so the compiler will not introduce an implicit reborrow, and will move ownership of r into the function in the first call instead, resulting in an error in the second function call.

The same thing happens in your code. The function to_writer() is declared as

pub fn to_writer<W, T: ?Sized>(writer: W, value: &T) -> Result<()>
where
    W: io::Write,
    T: Serialize,

Since the argument writer is not declared as a mutable reference, you need to create an explicit reborrow to avoid a move:

serde_json::to_writer(&mut *out, &1)?;

The alternative solution you gave in your question works as well – the write_wrapper() function receives an explicitly declared mutable reference as argument, so calls to this function trigger implicit reborrows.

Upvotes: 3

Related Questions