Reputation: 5242
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
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