如何使用 Serde 反序列化带有读者引用的结构?

leo*_*ion 4 yaml lifetime rust deserialization serde

我有这些结构:

#[derive(Debug, Serialize, Deserialize)]
pub struct GGConf<'a> {
    #[serde(alias = "ssh")]
    #[serde(rename = "ssh")]
    #[serde(default)]
    #[serde(borrow)]
    pub ssh_config: Option<SSHConfig<'a>>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct SSHConfig<'a> {
    #[serde(alias = "privateKey")]
    #[serde(rename = "privateKey")]
    private_key: &'a str,

    username: &'a str,
}
Run Code Online (Sandbox Code Playgroud)

当我从 YAML 文件读取时发生反序列化:

let mut config: GGConf = serde_yaml::from_reader(file)?;
Run Code Online (Sandbox Code Playgroud)

在编译时,我收到一个错误:

#[derive(Debug, Serialize, Deserialize)]
pub struct GGConf<'a> {
    #[serde(alias = "ssh")]
    #[serde(rename = "ssh")]
    #[serde(default)]
    #[serde(borrow)]
    pub ssh_config: Option<SSHConfig<'a>>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct SSHConfig<'a> {
    #[serde(alias = "privateKey")]
    #[serde(rename = "privateKey")]
    private_key: &'a str,

    username: &'a str,
}
Run Code Online (Sandbox Code Playgroud)

我隐约明白 serde 反序列化也有生命周期'de,编译器混淆了我为它指定的生命周期?如果我错了,请纠正我。

我目前如何正确地将 YAML 反序列化为两个结构?有什么我在这里遗漏或误解的吗?

我查看了如何使用 actix-web 的 Json 类型解决“serde::Deserialize 的实现不够通用”?,但我不能使用拥有的类型。我需要它是借来的类型。

我将尝试为此编写一个游乐场示例。

She*_*ter 9

这是不可能的; 您必须使用拥有的数据而不是引用。

这是一个最小的例子:

use serde::Deserialize; // 1.0.104

#[derive(Debug, Deserialize)]
pub struct SshConfig<'a> {
    username: &'a str,
}

fn example(file: impl std::io::Read) {
    serde_yaml::from_reader::<_, SshConfig>(file);
}
Run Code Online (Sandbox Code Playgroud)
use serde::Deserialize; // 1.0.104

#[derive(Debug, Deserialize)]
pub struct SshConfig<'a> {
    username: &'a str,
}

fn example(file: impl std::io::Read) {
    serde_yaml::from_reader::<_, SshConfig>(file);
}
Run Code Online (Sandbox Code Playgroud)

如果您查看 的定义serde_yaml::from_reader,您会发现它仅限于反序列化拥有的数据:

pub fn from_reader<R, T>(rdr: R) -> Result<T>
where
    R: Read,
    T: DeserializeOwned,
//     ^^^^^^^^^^^^^^^^ 
Run Code Online (Sandbox Code Playgroud)

这同样适用于serde_json::from_reader并且可能任何等效的功能。

当有数据要引用时,您只能反序列化包含引用的类型。实现Readtrait 的东西只能保证它可以将一些字节复制到用户提供的缓冲区中。由于该from_reader函数不接受该缓冲区作为参数,因此任何缓冲区都将在 退出时被销毁,从而from_reader使引用无效。

也可以看看:


如果您必须使用引用(在许多情况下并非如此),您将需要:

  1. 自己从阅读器读取到缓冲区
  2. 使用from_str代替from_reader
  3. 只要反序列化的数据就保持缓冲区


dbr*_*dbr 5

from_reader从某个地方(任何实现 Read 特征的地方)获取数据流 - 它不存储数据,这意味着没有任何东西拥有数据,因此您不能在结构中引用该数据。换句话说,from_reader需要一个临时的数据流,因此需要一个地方来存储数据。

另一个复杂因素是serde_yaml(至少对于版本 0.8.11)不支持零拷贝反序列化:

https://docs.rs/serde_yaml/0.8.11/serde_yaml/fn.from_str.html

pub fn from_str<T>(s: &str) -> Result<T> where
    T: DeserializeOwned,
Run Code Online (Sandbox Code Playgroud)

...

YAML 目前不支持零拷贝反序列化。

将其与比方说 相比serde_json,它执行以下操作:

https://docs.rs/serde_json/1.0.50/serde_json/de/fn.from_str.html

pub fn from_str<'a, T>(s: &'a str) -> Result<T> where
    T: Deserialize<'a>,
Run Code Online (Sandbox Code Playgroud)

因此,至少serde_json可以from_str从拥有的缓冲区中使用类似的东西,这将允许您在结构中使用引用(但这不适用于serde_yaml当前)

pub fn from_str<T>(s: &str) -> Result<T> where
    T: DeserializeOwned,
Run Code Online (Sandbox Code Playgroud)

完全取决于您所关心的是什么,这可能比在您的结构中存储字符串效率低(例如,如果example.json1MB 大,并且您只提取一个字段 - 上面的代码会将整个 1MB 字符串存储在内存中,只有几个字节的文本可访问)。