Как вызвать serde_json :: to_writer дважды с одним и тем же изменяемым заимствованием? - PullRequest
4 голосов
/ 10 октября 2019

Я пытаюсь написать функцию, которая дважды вызывает 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();  
}

1 Ответ

1 голос
/ 11 октября 2019

Причина такого поведения несколько неуловима. При передаче общей ссылки в качестве параметра функции, Rust просто скопирует ссылку. Тип &T равен Copy для всех T, поскольку нам разрешено иметь любое количество общих ссылок одновременно.

С другой стороны, изменяемые ссылки не являются Copy, поскольку в любой момент времени может быть только один из них. В соответствии с обычной семантикой Rust для не Copy типов это означает, что изменяемые ссылки должны перемещаться при передаче в качестве параметра. Итак, почему этот код работает?

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

, код перестанет компилироваться. Теперь параметр foo() больше не объявляется как изменяемая ссылка, поэтому компилятор не будет вводить неявное повторное заимствование и вместо этого перенесет владение 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() получает явно объявленную изменяемую ссылку в качестве аргумента, поэтому вызовы этой функции вызывают неявные перезагружения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...