Производная `Deserialize` с заимствованным полем структуры

#rust #macros #deserialization #traits

#Ржавчина #макросы #десериализация #Трейты

Вопрос:

Я пытаюсь вывести Deserialize из структуры, которая имеет заимствованный контент:

 #[macro_use]
extern crate serde_derive;
use std::net::SocketAddr;

#[derive(Hash, Eq, PartialEq, Debug, Clone, Serialize, Deserialize)]
pub struct MyS<'a> {
    pub addr: amp;'a SocketAddr,
}
  

игровая площадка rust

Я получаю следующую ошибку компиляции:

 error[E0277]: the trait bound `amp;'a std::net::SocketAddr: _::_serde::Deserialize<'_>` is not satisfied
 --> src/lib.rs:7:5
  |
7 |     pub addr: amp;'a SocketAddr,
  |     ^^^ the trait `_::_serde::Deserialize<'_>` is not implemented for `amp;'a std::net::SocketAddr`
  |
  = help: the following implementations were found:
            <std::net::SocketAddr as _::_serde::Deserialize<'de>>
  = note: required by `_::_serde::de::SeqAccess::next_element`
  

Какими различными способами я могу реализовать эту структуру Deserialize в течение некоторого срока службы?

Примечание: на самом деле я не требую, чтобы десериализация выполнялась с нулевой копией, это всего лишь приятный способ

Комментарии:

1. Десериализация в ссылку на самом деле невозможна, поскольку никто не владеет данными. Смотрите эту проблему на Github: github.com/serde-rs/serde/issues/1092

Ответ №1:

Вы не можете этого сделать, и вот почему:

Serde допускает десериализацию с нулевой копией определенных типов. В этом случае данные заимствуются из Deserializer , который, в свою очередь, либо заимствует, либо владеет каким-либо буфером. Поскольку amp;[u8] и amp;str оба являются по сути потоками байтов, их можно просто десериализовать как ссылки в буфер. Как именно это работает, объясняется в документах serde, но принципиально требуется, чтобы тип Rust отображался точно так, как данные существуют в буфере, который вы десериализуете.

В вашем случае, однако, вы хотите десериализовать SocketAddr . Проблема в том, что данные в буфере имеют форму, отличную от той, которую вы хотите заимствовать. Serde сериализует SocketAddr s в строку с помощью Display , смотрите, например, эту реализацию для SocketAddrV4 . Но SocketAddr само по себе хранится как 32-разрядное целое число и 16-разрядное целое число. Вы не можете заимствовать одно как другое; должен произойти какой-то синтаксический анализ, и кто-то затем должен владеть результирующими данными. Логически, что ваша структура владеет этими данными. Даже при двоичной сериализации нет гарантии, что расположение сериализованных данных будет соответствовать расположению структуры, поэтому синтаксический анализ должен выполняться.

Это закодировано в системе типов с помощью реализаций, которые Deserialize предоставляет serde. На этой странице приведен полный список. Вы можете видеть, что существует реализация, для amp;'a str которой время жизни Deserializer ( 'de ) истекает 'a . Нет реализации для ссылок на большинство других типов, включая amp;SocketAddr .

Эта проблема serde, на которую ссылается PitaJ, показывает хороший пример минимального кода.

Комментарии:

1. Спасибо за объяснение! Я видел намеки на то, что это невозможно, но все они относились к конкретным случаям, где я не понимал, действительно ли это применимо к общему случаю / к моему случаю.

2. «затем кому-то понадобятся результирующие данные». Я полагаю, вы имеете в виду «собственное»? Это своего рода ключевое предложение к вашему объяснению

3. Побочные вопросы: в случае десериализации в amp;'a str (что возможно), где Deserializer ( 'de ) сохраняется 'a , как десериализатор сохраняется до тех пор, пока сохраняется структура? В куче? Означает ли это, что он живет намного дольше, чем процесс десериализации? И если моя структура владеет своими полями, уничтожается ли десериализатор сразу после десериализации?

4. Да, я действительно имел в виду «собственное». Исправлено, спасибо! Что касается вопросов о времени жизни Deserializer , это будет зависеть от сопутствующей библиотеки serde. Вы заметите, что serde не определяет никаких реальных десерализаторов, только черты. В случае serde_json мы имеем, pub fn from_str<'a, T>(s: amp;'a str) -> Result<T> where T: Deserialize<'a> которое полностью пропускает время жизни 'de . Ваши данные заимствуются из самого буфера. Это также возможно с промежуточным десериализатором, по крайней мере, в этой библиотеке. Нажатие кнопки [src] в документах — отличный способ узнать, как это делается…

5. Но в целом, хорошо сделанная библиотека позволит вам удалить десериализатор и сохранить ссылку на буфер данных. Serde разработан, чтобы разрешить это. При уничтожении десериализатора всегда действуют обычные правила удаления Rust. Serde не требует выделения десериализаторов. Выделение произойдет, только если вы десериализуете в тип, который в этом нуждается, например Vec , HashMap или String .