首页 > 解决方案 > 如何使用相同的可变借用两次调用 serde_json::to_writer?

问题描述

我正在尝试编写一个调用serde_json::to_writer两次来编写两件事的函数,但我不知道怎么做。

这是一次尝试(游乐场链接):

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(())
}

编辑:想出了一种方法来编译,但有没有更简单的方法(游乐场链接):

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();  
}

标签: rust

解决方案


这种行为的原因有些微妙。当将共享引用作为参数传递给函数时,Rust 将简单地复制该引用。该类型&TCopyfor all T,因为我们可以同时拥有任意数量的共享引用。

另一方面,可变引用不是Copy,因为在任何给定时间只能有一个。根据非Copy类型的常用 Rust 语义,这意味着可变引用在作为参数传递时应该被移动。那么为什么这段代码有效呢?

fn foo(_: &mut i32) {}

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

原因是编译器在分配给显式声明为可变引用的变量时创建隐式重借foo(&mut *r),因此函数调用被转换为. 这将创建一个仅在函数调用期间持续的新借用,并且r一旦重新借用的生命周期结束,原始引用将再次可用。

但是,仅为使用可变引用类型显式声明的变量生成隐式重借。如果我们将foo()上面的定义更改为

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

代码将停止编译。现在参数 offoo()不再声明为可变引用,因此编译器不会引入隐式重借,r而是在第一次调用时将所有权转移到函数中,导致第二次函数调用出错。

您的代码中也会发生同样的事情。该函数to_writer()声明为

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

由于该参数writer未声明为可变引用,因此您需要创建显式重借以避免移动:

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

您在问题中提供的替代解决方案也适用 - 该write_wrapper()函数接收显式声明的可变引用作为参数,因此对该函数的调用会触发隐式重借。


推荐阅读