枚举上的 Serde 未标记属性导致堆栈溢出

Serde untagged attribute on enum results in stack overflow

我在枚举上使用 untagged 属性来序列化和反序列化 JSON。

// Just an example of using `untagged` - not actual code with the issue.
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
enum Message {
    String(String),
    X(X),
}

我的某些类型在运行时得到 fatal runtime error: stack overflow

这些类型可以使用 Box.

进行递归定义

一般来说,使用 untagged 属性时什么会导致堆栈溢出?

文档没有说明任何限制,因此它似乎适用于任何编译代码。

这是我在 Mac OS 上用 Instruments/Sampler 收集的堆栈跟踪,__rust_probestack 似乎是最后调用的函数,此时大约有 70 个函数深

根据你在评论中所说的,你有递归类型。问题来自 untagged 的工作方式。这是重现问题的最小示例:

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
enum Foo {
    Foo(Box<Foo>),
    Bar(String),
}

fn main() {
    let data = r#""Bar""#;

    let _v: Foo = serde_json::from_str(&data).unwrap();
}

Playground

问题在于,对于 untagged,Serde 尝试将数据解析为枚举的第一个变体,如果失败,它会回溯并尝试第二个变体,依此类推。以下是上面示例中发生的情况:

  1. Serde 被要求解析类型为 Foo 的值,该值未标记,因此 Serde 尝试解析与第一个变体匹配的值。
  2. 第一个变体是 Box<Foo>,因此 Serde 尝试解析类型为 Foo 的值,该值未标记,因此 Serde 尝试解析与第一个变体匹配的值。
  3. 第一个变体是 Box<Foo>,因此 Serde 尝试解析类型为 Foo 的值,该值未标记,因此 Serde 尝试解析与第一个变体匹配的值。
  4. 以此类推

请注意,这与输入数据无关,此代码甚至在尝试读取单个字节数据之前就失败了!

为避免此问题,您必须确保 Serde 在递归之前始终会消耗一些数据,例如:

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
enum Foo {
    Foo(String, Box<Foo>),
    Bar(String),
}

fn main() {
    let data = r#""Bar""#;

    let _v: Foo = serde_json::from_str(&data).unwrap();
}

Playground

https://github.com/serde-rs/serde/issues/1062#issuecomment-335659227

通过将堆栈设置为 16MB 来修复:

export RUST_MIN_STACK=16777216 && cargo test