枚举上的 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();
}
问题在于,对于 untagged
,Serde 尝试将数据解析为枚举的第一个变体,如果失败,它会回溯并尝试第二个变体,依此类推。以下是上面示例中发生的情况:
- Serde 被要求解析类型为
Foo
的值,该值未标记,因此 Serde 尝试解析与第一个变体匹配的值。
- 第一个变体是
Box<Foo>
,因此 Serde 尝试解析类型为 Foo
的值,该值未标记,因此 Serde 尝试解析与第一个变体匹配的值。
- 第一个变体是
Box<Foo>
,因此 Serde 尝试解析类型为 Foo
的值,该值未标记,因此 Serde 尝试解析与第一个变体匹配的值。
- 以此类推
请注意,这与输入数据无关,此代码甚至在尝试读取单个字节数据之前就失败了!
为避免此问题,您必须确保 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();
}
https://github.com/serde-rs/serde/issues/1062#issuecomment-335659227
通过将堆栈设置为 16MB 来修复:
export RUST_MIN_STACK=16777216 && cargo test
我在枚举上使用 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();
}
问题在于,对于 untagged
,Serde 尝试将数据解析为枚举的第一个变体,如果失败,它会回溯并尝试第二个变体,依此类推。以下是上面示例中发生的情况:
- Serde 被要求解析类型为
Foo
的值,该值未标记,因此 Serde 尝试解析与第一个变体匹配的值。 - 第一个变体是
Box<Foo>
,因此 Serde 尝试解析类型为Foo
的值,该值未标记,因此 Serde 尝试解析与第一个变体匹配的值。 - 第一个变体是
Box<Foo>
,因此 Serde 尝试解析类型为Foo
的值,该值未标记,因此 Serde 尝试解析与第一个变体匹配的值。 - 以此类推
请注意,这与输入数据无关,此代码甚至在尝试读取单个字节数据之前就失败了!
为避免此问题,您必须确保 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();
}
https://github.com/serde-rs/serde/issues/1062#issuecomment-335659227
通过将堆栈设置为 16MB 来修复:
export RUST_MIN_STACK=16777216 && cargo test