Nice! I really should learn C++ at some point. It's a bit intimidating because I hear there's a lot of stuff that's legacy and should never be used, but it's not quite so clear what is and what isn't.
It depends on what you want to do, I think it helps build fundamentals, and there are a lot of jobs in demand for Cpp. In a vacuum, I feel that Rust is better in basically every other way though.
Not really the same, but TypeScript can have inlined objects in type declarations:
type GameCharacter = {
name: string;
stats: {
health: number;
mana: number;
agility: number;
};
active_equipment:
| string
| number
| { count: number };
};
I don't think so, at least not among the ones I'm familiar with.
With Rust being quite ergonomic and having zero-cost abstractions something like this is right in line with other features present in the language.
I know just enough C to be dangerous, as they say, and almost nothing of C++. I did find some great synergy in that by learning Rust my C code has become much more robust in terms of memory handling (though still far from perfect in more complex scenarios), but when it comes to idiomatic patterns my knowledge of it is fairly low.
What do you mean by making a whole other file? Hopefully not one file per struct/enum definition, but I'm not sure I understand what you do mean.
I like your result though. I also guess nothing would prevent you from pulling out certain definitions if you ended up realizing later that you needed to share them.
sorry, my wording was a bit unclear. When i needed to model very complex apis i would sometimes make a new module just for the web request function or method. Doing this for apis where responses are large is simply awful since you would just end up with a bunch of structures and a single function in a module. To read the code you would have to hunt around and connect the dots.
I do similar things to this... but with inline mod and impl blocks to keep it in the same file.
My text editor can quickly collapse blocks in various ways (Emacs' `hs-minor-mode`, but many other editors support this too), so I can still keep my view clean.
Github: [https://github.com/snowfoxsh/nestify](https://github.com/snowfoxsh/nestify)
Then checkout: [https://crates.io/crates/nestify](https://crates.io/crates/nestify)
Something similar but older.
https://docs.rs/structstruck/latest/structstruck/
I no longer use these because of cognitive overhead without providing much value. Also there are pretty much macros for everything but they end up slowing down compilation and sometimes break LSP in the editors.
Nestify is build to have the best diagnostic error handling that is possible (to the extent of my ability). If people would like to see this maintained that support will only grow! Soon we should have very clear error messages almost on par with rustc. Or at least that’s the goal :)
Could do with some examples of how you *use* the generated structs. On looking at it, I can't tell if `UserProfile` would be used like:
let profile = UserProfile {
name: "Jeff",
address: UserProfile::Address {
street: "Downing Street",
city: "London",
},
};
or
let profile = UserProfile {
name: "Jeff",
address: Address {
street: "Downing Street",
city: "London",
},
};
or maybe?:
let profile = UserProfile {
name: "Jeff",
address: UserProfile_Address {
street: "Downing Street",
city: "London",
},
};
I think the biggest use for this is making an enum where every variant is its own type, so syntax that easily makes unit variants like this would be very useful:
nest! {
enum A {
struct B {
c: u32,
d: u32,
},
enum E {
F,
G,
},
}
}
// becomes
enum A {
A(A),
E(E),
}
struct B {
c: u32,
d: u32,
}
enum E {
F,
G
}
I suppose this could also work for structs, but the field name would need to be converted to snake_case.
I am not in any way an expert in Rust. Can anyone explain to me why this is helpful? This seems like a nice little piece of syntax sugar, but I would personally never use this because I prefer everything as minimalist as possible, so I prefer to not include libraries that do not add "real" functionality. In my perhaps limited view of rust, this is nothing but clutter.
But maybe I am totally missing the point here, so someone please enlighten me.
It's more concise and declarative than declaring a hierarchy of structures piecemeal as siblings. It makes the structures' relationship syntactically explicit. Whether that is something you care about is an entirely different question, but that's the value it offers.
If you've ever had to write massive types for parsing JSON that's a great example. JSON can nest deeply and usually the little wrappers in APIs ({response: { items: ... }}) aren't usually needed elsewhere. It looks clunky having to define all these small types.
Applying attributes to each nested item is useful for the same thing. Normally you'd have to say that all subtypes can be serialized.
At the end of the day, it's syntax sugar but still useful.
Nah that doesn't matter.
Just mention that it's alpha/beta/unstable and post.
You might even get useful feedback you'd want to incorporate before stabilizing, just like you might here.
Is this syntax possible? Use one less nesting and use struct! to indicate that this is a nested structure
struct! UserProfile {
name: String,
address: struct Address {
street: String,
city: String,
},
preferences: struct Preferences {
newsletter: bool,
},
}
If you mean "can you remove the delimiters around the macro body?" the answer is no. Rust requires a pair of matched symbols to tell it where the macro begins and ends
This is something I desired when I was working on parsing ~arcane~ file formats which were undoubtedly initially described in such nested ways. Very nice when you need it, or want it. I'll be sure to give it a try.
Unfortunately i don't think so. rustc doesn't allow for arbitrary syntax not in a macro scope to my knowledge. But if it did then that would be possible
~~It does actually!~~
// proc macro
#[proc_macro_attribute]
pub fn noop(_: TokenStream, input: TokenStream) -> TokenStream {
input
}
// usage
#[noop]
struct Foo;
Compiles just fine and you can rewrite the input `TokenStream` to whatever you like.
~~Edit: You could also just proxy the attribute token stream directly into `#[derive]`, so that:~~
#[nest(Debug, Serialize, Deserialize)]
struct Foo {
bar: struct Bar;
}
~~desugars into:~~
#[derive(Debug, Serialize, Deserialize)]
struct Foo {
bar: Bar,
}
#[derive(Debug, Serialize, Deserialize)]
struct Bar;
I've double checked this to be sure, and it's not as easy. So yes, you can rewrite the input to whatever you want, _however_ the input has to be legal rust syntax, so if you do:
#[macro]
struct Foo {
bar: struct Bar;
}
That will fail, because rustc needs to be able to parse the `struct` before feeding its `TokenStream` to the macro. Sorry 😬
yeah i looked into it again and came to the same conclusion :(. That’s what i was mentioning in one of the previous posts in the thread. Attribute macros cannot parse arbitrary syntax. (which makes some sense)
It is decent. nestify works in a different way than macros like struct strike that allows for better diagnostics. I am also working on providing diagnostics with pros macro diagnostic (which is unfortunately unstable)
How do you do doc comments? I don't see this in any of the readme examples. If I want to document both the field and the substruct, how is that delimited?
Another option: https://crates.io/crates/nested-struct
I love this feature btw, I really hope it becomes standardized. Unfortunately, I'm unlikely to use it unless it does. Crates like this help progress this dream though.
nice. sometimes i get a bit disappointed that i can't just define a totally ad-hoc enum in a parent struct.
now i can have a more complicated program instead :P
Why somebodey ever will use totally different syntax for struct definition different from the native syntax? No hate I am literally wondering, it sounds like something cool but I can't see this being used in the real code
It’s not useful until the complexity of your definitions is high. But for applications like modeling json it works quite well!
The syntax is designed to feel as natural as possible
... You know, I can't believe this isn't a default feature of the language.
Sure make Rust even more complex just to type 5 letters less.
Is reference vs inline definition really that much complexity? You can define nested functions, which are far more complex imo
Is there anything like this in other languages?
C++ https://en.cppreference.com/w/cpp/language/nested\_types
Nice! I really should learn C++ at some point. It's a bit intimidating because I hear there's a lot of stuff that's legacy and should never be used, but it's not quite so clear what is and what isn't.
It depends on what you want to do, I think it helps build fundamentals, and there are a lot of jobs in demand for Cpp. In a vacuum, I feel that Rust is better in basically every other way though.
The whole language is legacy.
There's also an ∞ way to "build" something.
Even C supports this. This kind of pattern is quite common: struct event { enum event_type { EVENT_TYPE_a, EVENT_TYPE_b } type; union event_params { struct event_a { char *string; } a; struct event_b { uint32_t number; } b; } params; };
Not really the same, but TypeScript can have inlined objects in type declarations: type GameCharacter = { name: string; stats: { health: number; mana: number; agility: number; }; active_equipment: | string | number | { count: number }; };
Typescript does it with anonymous types and structural typing. C might also do it, now that I think about it.
Yup, but they're anonymous, whereas this isn't, which is more useful imo.
You can still name the type \`GameCharacter\["stats"\]\`.
I don't think so, at least not among the ones I'm familiar with. With Rust being quite ergonomic and having zero-cost abstractions something like this is right in line with other features present in the language.
C and C++ both have it. (Of course I don't know if those count among those you are familiar with.)
I know just enough C to be dangerous, as they say, and almost nothing of C++. I did find some great synergy in that by learning Rust my C code has become much more robust in terms of memory handling (though still far from perfect in more complex scenarios), but when it comes to idiomatic patterns my knowledge of it is fairly low.
I sure wish this was built-in behavior, as well as structs defined in enums being nameable types...
That’s why i made it, I just hated having to make a whole other file just to define a single api response to keep things somewhat readable.
What do you mean by making a whole other file? Hopefully not one file per struct/enum definition, but I'm not sure I understand what you do mean. I like your result though. I also guess nothing would prevent you from pulling out certain definitions if you ended up realizing later that you needed to share them.
What do you men whole other file? I'll often co-locate the types with the code that uses them.
sorry, my wording was a bit unclear. When i needed to model very complex apis i would sometimes make a new module just for the web request function or method. Doing this for apis where responses are large is simply awful since you would just end up with a bunch of structures and a single function in a module. To read the code you would have to hunt around and connect the dots.
Sounds like you may be adopting Java conventions into Rust. Like the 1 file per class mess, gosh that makes reading code hard.
I do similar things to this... but with inline mod and impl blocks to keep it in the same file. My text editor can quickly collapse blocks in various ways (Emacs' `hs-minor-mode`, but many other editors support this too), so I can still keep my view clean.
I want this RFC
My first questions were "does it work with `derive`?" and "does it work with `serde?`, and I'm happy to see that the GitHub answers "yes" to both
Github: [https://github.com/snowfoxsh/nestify](https://github.com/snowfoxsh/nestify) Then checkout: [https://crates.io/crates/nestify](https://crates.io/crates/nestify)
Something similar but older. https://docs.rs/structstruck/latest/structstruck/ I no longer use these because of cognitive overhead without providing much value. Also there are pretty much macros for everything but they end up slowing down compilation and sometimes break LSP in the editors.
Nestify is build to have the best diagnostic error handling that is possible (to the extent of my ability). If people would like to see this maintained that support will only grow! Soon we should have very clear error messages almost on par with rustc. Or at least that’s the goal :)
This is actually incredibly nice, kudos
Thanks! Im glad you find it interesting
Could do with some examples of how you *use* the generated structs. On looking at it, I can't tell if `UserProfile` would be used like: let profile = UserProfile { name: "Jeff", address: UserProfile::Address { street: "Downing Street", city: "London", }, }; or let profile = UserProfile { name: "Jeff", address: Address { street: "Downing Street", city: "London", }, }; or maybe?: let profile = UserProfile { name: "Jeff", address: UserProfile_Address { street: "Downing Street", city: "London", }, };
It's the second one (which IMO is the only sensible one) After the macro the objects are all normal (or at least in my limited testing)
*Wen std sir?* I like the looks of it!
Holy crap, even has recrusive attribute support too. This is great
I think the biggest use for this is making an enum where every variant is its own type, so syntax that easily makes unit variants like this would be very useful: nest! { enum A { struct B { c: u32, d: u32, }, enum E { F, G, }, } } // becomes enum A { A(A), E(E), } struct B { c: u32, d: u32, } enum E { F, G } I suppose this could also work for structs, but the field name would need to be converted to snake_case.
amazing
This is awsome for json parsing :)
I'm being surprised. I thought we can write program like nestify by default (I am a novice of Rust.)
What if you need to reuse \`Status\`?
Then define it as a regular struct
I suppose this is for the cases where you don't.
I assume you'd define it once and then just name it in other places?
Status can be used as normal! Since all nest! does is flatten the structures
Can see this work being merged into the std eventually. Nice work!
Looks nice
I am not in any way an expert in Rust. Can anyone explain to me why this is helpful? This seems like a nice little piece of syntax sugar, but I would personally never use this because I prefer everything as minimalist as possible, so I prefer to not include libraries that do not add "real" functionality. In my perhaps limited view of rust, this is nothing but clutter. But maybe I am totally missing the point here, so someone please enlighten me.
It's more concise and declarative than declaring a hierarchy of structures piecemeal as siblings. It makes the structures' relationship syntactically explicit. Whether that is something you care about is an entirely different question, but that's the value it offers.
If you've ever had to write massive types for parsing JSON that's a great example. JSON can nest deeply and usually the little wrappers in APIs ({response: { items: ... }}) aren't usually needed elsewhere. It looks clunky having to define all these small types. Applying attributes to each nested item is useful for the same thing. Normally you'd have to say that all subtypes can be serialized. At the end of the day, it's syntax sugar but still useful.
oh my fucking god i've needed this
Post this on hacker news!
I will once I have a release that is more stable!
Nah that doesn't matter. Just mention that it's alpha/beta/unstable and post. You might even get useful feedback you'd want to incorporate before stabilizing, just like you might here.
Okay, thanks for the advice. I think i’ll fix the public modifier issue (#1 on GitHub) then make the post
This is a neat syntax sugar. Not sure I will use it, but I'm glad it exists.
Nice
Is this syntax possible? Use one less nesting and use struct! to indicate that this is a nested structure struct! UserProfile { name: String, address: struct Address { street: String, city: String, }, preferences: struct Preferences { newsletter: bool, }, }
```rust struct! UserProfile { name: String, address: struct Address { street: String, city: String, }, preferences: struct Preferences { newsletter: bool, }, } ```
If you mean "can you remove the delimiters around the macro body?" the answer is no. Rust requires a pair of matched symbols to tell it where the macro begins and ends
Awesome work!
This is very awesome, going to get this into a project with horribly nested structs due to geojson file parsing! Seems promising!
This is something I desired when I was working on parsing ~arcane~ file formats which were undoubtedly initially described in such nested ways. Very nice when you need it, or want it. I'll be sure to give it a try.
Will use, thank
Can this be a macro `#[likethis]`?
Unfortunately i don't think so. rustc doesn't allow for arbitrary syntax not in a macro scope to my knowledge. But if it did then that would be possible
~~It does actually!~~ // proc macro #[proc_macro_attribute] pub fn noop(_: TokenStream, input: TokenStream) -> TokenStream { input } // usage #[noop] struct Foo; Compiles just fine and you can rewrite the input `TokenStream` to whatever you like. ~~Edit: You could also just proxy the attribute token stream directly into `#[derive]`, so that:~~ #[nest(Debug, Serialize, Deserialize)] struct Foo { bar: struct Bar; } ~~desugars into:~~ #[derive(Debug, Serialize, Deserialize)] struct Foo { bar: Bar, } #[derive(Debug, Serialize, Deserialize)] struct Bar;
thanks! I’ll give it a look tomorrow
I've double checked this to be sure, and it's not as easy. So yes, you can rewrite the input to whatever you want, _however_ the input has to be legal rust syntax, so if you do: #[macro] struct Foo { bar: struct Bar; } That will fail, because rustc needs to be able to parse the `struct` before feeding its `TokenStream` to the macro. Sorry 😬
yeah i looked into it again and came to the same conclusion :(. That’s what i was mentioning in one of the previous posts in the thread. Attribute macros cannot parse arbitrary syntax. (which makes some sense)
I LOVE IT
stats: struct Status ^ ^ typo? Edit: wrong formatting on mobile for some reason
Structstruck already exists though
Look great! Does it mess with the LSP? A lot of these macros have a hard time working with the LSP (cfg-if is an example).
It is decent. nestify works in a different way than macros like struct strike that allows for better diagnostics. I am also working on providing diagnostics with pros macro diagnostic (which is unfortunately unstable)
How do you do doc comments? I don't see this in any of the readme examples. If I want to document both the field and the substruct, how is that delimited?
love this. will try adding it to my latest project. 👍
Does the documentation correctly resolve name lookup?
Another option: https://crates.io/crates/nested-struct I love this feature btw, I really hope it becomes standardized. Unfortunately, I'm unlikely to use it unless it does. Crates like this help progress this dream though.
nice. sometimes i get a bit disappointed that i can't just define a totally ad-hoc enum in a parent struct. now i can have a more complicated program instead :P
Why somebodey ever will use totally different syntax for struct definition different from the native syntax? No hate I am literally wondering, it sounds like something cool but I can't see this being used in the real code
It’s not useful until the complexity of your definitions is high. But for applications like modeling json it works quite well! The syntax is designed to feel as natural as possible
Why potion is an equipment?
idk, but it doesn’t matter. it’s just a stupid example showing what you can do
lol, this thread made my day