Why toml to_string() get error ValueAfterTable?

I have a json:

{
    "sync": {
        "storage_type": "",
        "local_disk": {
            "data_dir_path": ""
        },
        "interval_seconds": 6
    }
}

if I just parse this json string,it works OK:

let r= r##"
{
    "sync": {
        "storage_type": "",
        "local_disk": {
            "data_dir_path": ""
        },
        "interval_seconds": 6
    }
}
"##;
 

match toml::to_string(&r) {
    Ok(toml) => {
        print!(">>> toml: {:?} \n", toml)
    }
    Err(e) => {
        print!(">>> toml error: {:?} \n", e)
    }
}

but,if I parse this json string into a struct,then convert the struct into toml string,it maybe cause an ValueAfterTable error.

Before convert into toml,print the struct converted from json string:

Conf { sync: Sync { storage_type: "", local_disk: SyncLocalDisk { data_dir_path: "" } } }

It as same as the json string.

The struct is:

#[derive(Serialize, Deserialize, Debug)]
struct SyncLocalDisk {
    data_dir_path: String,
}

#[derive(Serialize, Deserialize, Debug)]
struct Sync {
    storage_type: String,
    local_disk: SyncLocalDisk, // After comment this line or the flowlling line, there will have no error
    interval_seconds: u32, 
}

#[derive(Serialize, Deserialize, Debug)]
struct Encryption {
    master_password: String,
    entry_file_name: String,
    file_ext: String,
    enable_file_compress: bool,
}

#[derive(Serialize, Deserialize, Debug)]
struct Conf {
    sync: Sync,
}

Or we can create a new Conf,then convert it, the result is the same :

let local_disk = SyncLocalDisk {
    data_dir_path: "path".to_string(),
};

let sync = Sync {
    storage_type: "storage".to_string(),
    local_disk: local_disk,
    interval_seconds: 8,                
};

let ccc = Conf { sync: sync };

match toml::to_string(&ccc) {
    Ok(toml) => {
        print!(">>> toml: {:?} \n", toml)
    }
    Err(e) => {
        print!(">>> toml error: {:?} \n", e)
    }
}

I have test with 0.5.9 and 0.5.10.

What's the reason? And how to solve it?

Thankyou

Minimal example with the same problem:

use toml::to_string;
use serde::Serialize;

#[derive(Default, Serialize)]
struct Inner {
    inner_field: i32,
}

#[derive(Default, Serialize)]
struct Outer {
    inner: Inner,
    outer_field: i32,
}

fn main() {
    println!("{:?}", to_string(&Outer::default()));
}

Playground

The error documentation seems to imply that this is the limitation of format - TOML can't have any subtables inside the table, by construction; in other words, all simple fields, like numbers and strings, must be written before the complex types like structs (which are mapped to tables).

This is relatively easy to fix if you control the struct definition - you have to change the order in which fields are serialized. In most cases, that means reordering them in the struct itself: if, in the code above, the Outer struct is instead

#[derive(Default, Serialize)]
struct Outer {
    outer_field: i32,
    inner: Inner,
}

then everything works fine.

Here is a demo code that use toml_edit instead of toml for right now if do not chang the struct,Why error ValueAfterTable caused? · Issue #395 · toml-rs/toml · GitHub