-
Notifications
You must be signed in to change notification settings - Fork 89
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Macros one dot one (Do Not Merge) #28
Conversation
Woohoo! But I really want to merge this right now! 😅 It might make sense to release this as an alternative version (if it's any good, I haven't read your code yet!). The macro derive code should be semi-stable in nightly -- diesel 0.8 pretty much promises you can use the latest nightly/syntex and it will work. Hw much work will testing two crates be? I.e., we have macro_rules & macros1.1 and a set of integration tests that work for both. |
After I pushed this I thought about having both code parts behind feature flags. But then again, this seems less optimal. We could however make it a derive-builder-next package to be released now and merge both back when Macros 1.1 hit stable. |
I would like to talk with you guys about renaming things to I had this thought for quite a while and I think this is a good moment to Jan-Erik Rediger notifications@github.com schrieb am Di., 11. Okt. 2016,
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
(I saw you commented on some stuff while I was reviewing this. Will respond in another comment.)
@@ -4,13 +4,17 @@ version = "0.2.1" | |||
authors = ["Colin Kiegel <kiegel@gmx.de>", | |||
"Pascal Hertleif <killercup@gmail.com>"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this gets merged you should be listed here, @badboy!
|
||
**This is a work in progress.** Use it at your own risk. | ||
**This is a work in progress.** Use it at your own risk. | ||
**This currently requires Rust nightly, due to the usage of Macros 1.1** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please mention which nightly you think works; something like "requires Rust nightly (2016-10-09 or newer)" is good. Also: Period at the end.
@@ -70,7 +69,6 @@ The builder pattern is explained [here][builder-pattern], including its variants | |||
|
|||
[doc]: https://colin-kiegel.github.io/rust-derive-builder | |||
[rust]: https://www.rust-lang.org/ | |||
[custom_derive]: https://crates.io/crates/custom_derive |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was fun while it lasted 😢
#[test] | ||
fn annotations() { | ||
// this is currently just a compile-test (may switch to token comparisons here) | ||
// https://github.com/colin-kiegel/rust-derive-builder/issues/19 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still valid? Is there a test story for nicely comparing syn tokens?
//! ```rust | ||
//! #[macro_use] extern crate custom_derive; | ||
//! #[macro_use] extern crate derive_builder; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you also add the #![feature(proc_macro)]
here (or does that not compile)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need to try.
|
||
#[proc_macro_derive(Builder)] | ||
pub fn derive(input: TokenStream) -> TokenStream { | ||
let input: String = input.to_string(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably unnecessary : String
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed. that bit was copy'n'pasted
self.#f_name = value.into(); | ||
self | ||
}) | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice & concise! 👍
IIRC* quote!
returns Tokens
which has an append
method. For future extensions we could add stuff like
let le_tokens = quote!(); // empty Tokens
if cfg!(feature = "consuming") {
le_tokens.append(quote!(/* what you wrote */))
} else { /* other stuff */}
if cfg!(feature = "getters") { /* getters, maybe filtered by some attr on the field */ }
* by which I mean "read https://docs.rs/quote/0.3.3/quote/struct.Tokens.html correctly after not trusting my memory"
}); | ||
|
||
quote! { | ||
impl #impl_generics #name #ty_generics #where_clause { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will this render as impl <T> Stuff <T> where T: Magic
-- with the spaces? If I was just one tiny bit more crazy I'd ask you to get rid of the spaces before the generics lists 😆
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have no idea, I just copy'n'pasted that bit and it worked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technical details which in no way affect my preference about where to put spaces:
-
From the
quote!
macro's perspective there is no way for it to tell where you put spaces. It sees aVec<TokenTree>
which looks like["impl", "#", "impl_generics", "#", "name", "#", "ty_generics", "#", "where_clause", Braces(["#", Parens(["funcs"]), "*"])]
. -
The string constructed by the
quote!
macro will have a single space in between every token (at least as of quote 0.3.3; I think in this version there might be more than one in certain cases). This is the simplest heuristic that is always correct with respect to rustc being able to parse the result as intended. So ifimpl_generics
is<T>
then the string will start with `"impl < T >". -
The builtin
stringify!
has the same behavior for the same reason:fn main() { // impl # impl_generics # name { # ( funcs ) * } println!("{}", stringify!(impl #impl_generics #name { #(funcs)* })); }
-
When you take the string from
quote!
and parse it to a TokenStream for #[derive(Builder)] to return, the spaces are throw out again. The TokenStream looks like["impl", "<", "T", ">", ...]
. -
One of the first things Rust does upon getting back the TokenStream is parse it. So if you
cargo expand
a crate containing #[derive(Builder)] the spaces will be where Rust's pretty-printer puts them, not wherequote!
puts them and not where you put them.
Nontechnical details which fully determine my preference:
- I find
impl#impl_generics #name#ty_generics
less readable. - I prefer the style @badboy used here.
- He probably got it from my examples so it is no coincidence 😛.
Bottom line:
- Put spaces wherever you want.
- There are so many steps between where you put the spaces and where the spaces end up that even if most of them mattered, it still wouldn't matter.
PS: if we go down this road we should add options to support different setter styles |
@badboy instead of a new crate with a new name we could also do a "derive-builder v0.3.0.alpha-1" release or something like that (if @colin-kiegel wants to do that). @colin-kiegel Interesting! I'd use the plural names though: It might also be valuable to introduce a whole new level of 'indirect builder': By defining a new struct |
@killercup Good points. :-) If you both like the idea of You may feel free to secure one set of crate names you like by publishing dummy crates. Because I'd guess it may be a popular exercise to implement this kind of stuff right now. PS: Or if you want me to do it, I could just switch to my dev partition and do it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a massive improvement, nicely done.
}); | ||
|
||
quote! { | ||
impl #impl_generics #name #ty_generics #where_clause { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technical details which in no way affect my preference about where to put spaces:
-
From the
quote!
macro's perspective there is no way for it to tell where you put spaces. It sees aVec<TokenTree>
which looks like["impl", "#", "impl_generics", "#", "name", "#", "ty_generics", "#", "where_clause", Braces(["#", Parens(["funcs"]), "*"])]
. -
The string constructed by the
quote!
macro will have a single space in between every token (at least as of quote 0.3.3; I think in this version there might be more than one in certain cases). This is the simplest heuristic that is always correct with respect to rustc being able to parse the result as intended. So ifimpl_generics
is<T>
then the string will start with `"impl < T >". -
The builtin
stringify!
has the same behavior for the same reason:fn main() { // impl # impl_generics # name { # ( funcs ) * } println!("{}", stringify!(impl #impl_generics #name { #(funcs)* })); }
-
When you take the string from
quote!
and parse it to a TokenStream for #[derive(Builder)] to return, the spaces are throw out again. The TokenStream looks like["impl", "<", "T", ">", ...]
. -
One of the first things Rust does upon getting back the TokenStream is parse it. So if you
cargo expand
a crate containing #[derive(Builder)] the spaces will be where Rust's pretty-printer puts them, not wherequote!
puts them and not where you put them.
Nontechnical details which fully determine my preference:
- I find
impl#impl_generics #name#ty_generics
less readable. - I prefer the style @badboy used here.
- He probably got it from my examples so it is no coincidence 😛.
Bottom line:
- Put spaces wherever you want.
- There are so many steps between where you put the spaces and where the spaces end up that even if most of them mattered, it still wouldn't matter.
There seems to be a way to use the I'm not sure if it's worth investigating, since macros 1.1 should not be too far away now. |
Any plan on finishing/merging this? 1.15 with stable macros 1.1 will be released on February 3rd, and I'd be delighted to ship a new, shiny derive-builder version on day one :) |
Good point. I'll try to look into updating this PR this weekend. |
So today I read up on the new Macros 1.1 approach and it turns out it is really easy to use (together with the
syn
andquote
crates).I ported over the code to use Macros 1.1 and it works with significantly less code now.
I had to move the tests into their own crate, compiling a test binary in the right library mode is impossible.
This makes the code require a recent nightly, DO NOT MERGE immediately
This can probably stay around until Macros 1.1 are actually stable (probably not before the end of the year though).