Reputation: 1472
Im using the untagged
attribute on an enum to serialize and deserialize JSON.
// Just an example of using `untagged` - not actual code with the issue.
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
enum Message {
String(String),
X(X),
}
I get a fatal runtime error: stack overflow
for some of my types at runtime.
These types may have recursive definitions by using Box
.
In general what would cause a stack overflow when using an untagged
attribute?
The docs do not state any limitations so it seems like it should work for any code that compiles.
This is a stack trace I collected with Instruments/Sampler on Mac OS, __rust_probestack
seems to be the last function called, at that point it's around 70 functions deep.
Upvotes: 2
Views: 6554
Reputation: 1472
https://github.com/serde-rs/serde/issues/1062#issuecomment-335659227
Fixed by setting the stack to 16MB:
export RUST_MIN_STACK=16777216 && cargo test
Upvotes: 1
Reputation: 23359
According to what you said in the comments, you have recursive types. The problem comes from the way untagged
works. Here's a minimal example that reproduces the problem:
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();
}
The problem is that with untagged
, Serde tries to parse the data as the first variant of the enum, then if that fails it backtracks and tries the second variant and so on. Here's what happens with the example above:
Foo
, which is untagged, so Serde tries to parse a value matching the first variant.Box<Foo>
, so Serde tries to parse a value of type Foo
, which is untagged, so Serde tries to parse a value matching the first variant.Box<Foo>
, so Serde tries to parse a value of type Foo
, which is untagged, so Serde tries to parse a value matching the first variant.Note that this has nothing to do with the input data, this code fails before even attempting to read a single byte of data!
To avoid the issue, you must make sure that Serde will always consume some data before recursing, eg.:
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();
}
Upvotes: 4