-
Notifications
You must be signed in to change notification settings - Fork 123
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
Redesign From
derive macro
#241
Conversation
@tyranron @JelteF as I was reworking
#[derive(From)]
enum Value {
#[from(forward)]
Int(i32),
String(String),
}
// Expands to
#[automatically_derived]
impl<__FromT0> ::core::convert::From<(__FromT0)> for Value
where
i32: ::core::convert::From<__FromT0>,
{
#[inline]
fn from(original: (__FromT0)) -> Value {
Value::Int(<i32 as ::core::convert::From<__FromT0>>::from(original))
}
} #[derive(From)]
enum Value {
#[from(types(i8))]
Int(i32),
String(String),
}
// Expands to
#[automatically_derived]
impl ::core::convert::From<(i32)> for Value {
#[inline]
fn from(original: (i32)) -> Value {
Value::Int(original)
}
}
#[automatically_derived]
impl ::core::convert::From<(i8)> for Value {
#[inline]
fn from(original: (i8)) -> Value {
Value::Int(<i32 as ::core::convert::From<i8>>::from(original))
}
} It's possible to manually specify other fields with #[derive(From)]
enum Value {
#[from(forward)]
Int(i32),
#[from]
String(String),
}
#[derive(From)]
enum Value {
#[from(forward)]
Int(i32),
#[from(forward)]
String(String),
}
#[derive(From)]
#[from(types(u8))]
struct Int(u64);
#[automatically_derived]
impl ::core::convert::From<(u64)> for Int {
#[inline]
fn from(original: (u64)) -> Int {
Int(original)
}
}
#[automatically_derived]
impl ::core::convert::From<(u8)> for Int {
#[inline]
fn from(original: (u8)) -> Int {
Int(<u64 as ::core::convert::From<u8>>::from(original))
}
}
#[derive(From)]
#[from(types(i8))]
struct Point(i32, i32);
#[automatically_derived]
impl ::core::convert::From<(i32, i32)> for Point {
#[inline]
fn from(original: (i32, i32)) -> Point {
Point(original.0, original.1)
}
}
#[automatically_derived]
impl ::core::convert::From<(i8, i8)> for Point {
#[inline]
fn from(original: (i8, i8)) -> Point {
Point(
<i32 as ::core::convert::From<i8>>::from(original.0),
<i32 as ::core::convert::From<i8>>::from(original.1),
)
}
} |
This is intended behavior. Do you have better suggestions?
The same here. It was in the initial design to work with tuples. |
👍
Explicitly including field type
Looks very counterintuitive and niche to me, I would prefer something like: #[derive(From)]
#[from(types((i8, i8), (i16, i16)))]
struct Point(i32, i32); |
This may have sense, since makes things more straightforward and allows more (excluding direct underlying type from impls).
But here I'm not sure what it gives except making ergonomics worse. Anyway, we need @JelteF's opinion on that. |
Thinking back, I think my original reasoning was that I liked it that you could easily specify variants explicitly by specifying the #[derive(From)]
#[from]
enum Value {
#[from(forward)]
Int(i32),
String(String),
}
I like that making it explicit you can choose not to derive the From for the actual type. But I'm strongly wondering how often that's applicable in practice. I think most people will want to derive additive for types. No-one has opened an issue so far that they couldn't do it. And maybe the better way to do so would be to allow
This one I actually think makes sense to change. There's many types you currently cannot specify. And strangely it doesn't even error if you currently pass a tuple. It simply doesn't derive anything and completely ignores the tuple. One example that I'd like to be able to derive is: #[derive(From)]
#[from(types(i8, &str))]
struct SomeMixedType(i32, String); To summarize: I'd keep 1 and 2 as is, but 3 I think makes sense to change. |
I think that combination of #[derive(From)]
enum Value {
#[from(ignore, types(i8))]
Int(i32),
#[from(ignore)]
String(String),
} And even if user understands whats going on here, discovering that on their own is quite unlikely. |
|
For 3.i, I don't have a real strong preference either way. But I'd lean towards keeping the current, behaviour simply because we haven't had complaints about it. |
I would prefer to not have such implying. It definitely makes sense for
I'd like to go with "implicitly disable the underlying type, requiring to specify it explicitly if necessary". Seems like a quite intuitive and unambiguous thing: either we default to the underlying type, or enumerate all the needed types explicitly. It also frees our hands to go forward and make more simplifications like this (removing the #[derive(From)]
enum Value {
#[from(i8, &i32)]
Int(i32),
#[from(&str, &mut str, &String, String)]
String(String),
}
#[derive(Into)]
#[into(i64, &i64, &mut i64)]
struct MyInt64(i64); |
I agree with both points.
|
Yes, I've thought about it a bit more, and have concluded that we cannot actually throw However, nothing bad happens if we strip |
From
derive macro From
derive macro attributes
From
derive macro attributesFrom
derive macro
This PR is ready for review |
- The `From` derive now uses `#[from(<types>)]` instead of `#[from(types(<types>))]` | ||
and ignores field type itself. |
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.
Hmm, I didn't realize this changed until I read this changelog entry. Sorry for not noticing earlier, because it's indeed in the PR description. But I don't think we should remove the types
wrapper itself. This will make it hard for us to add new sub-attributes to the from
attribute in a backwards compatible manner in the future, because although unlikely they might conflict with type names.
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.
To be clear this comment was about removing the types
wrapper. "Ignoring the field type itself" I'm also not a huge fan of, but I think that's fine.
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.
Overall I like this change a lot! Thanks for writing all the detailed tests too! Left one comment on the final syntax.
@ilslv small ping. I think this can be merged after the |
@JelteF I'll pick @ilslv's work up and finish it in next few days, since @ilslv is having vacations this week 🏝 But before doing so, I'd like to argue a little bit more about |
@tyranron I'm definitely interested in this:
(I guess you were busy with other things, which is totally fine, but just a kind reminder) |
@JelteF yes, sorry for such delaying, and thank you for the patience! Now, to the topic... As you've mentioned before:
Yes, since Rust types are But more to that (my main concern): #[derive(From)]
enum Value {
#[from(i8, i32)]
Int(i32),
#[from(f32, f64)]
Float(f64),
#[from(MyString, String)]
String(String),
#[from(skip)]
Ignored(String),
} #[derive(From)]
enum Value {
#[from(types(i8, i32))]
Int(i32),
#[from(types(f32, f64))]
Float(f64),
#[from(types(MyString, String))]
String(String),
#[from(skip)]
Ignored(String),
} |
Fair enough. Let's do it it without the types wrapper. |
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 you fix the mere conflicts I think we can merge this.
# Conflicts: # CHANGELOG.md # impl/src/fmt/mod.rs
# Conflicts: # tests/display.rs # tests/from.rs
## Synopsis This PR is a part of replacing all attributes having `syn::Meta` syntax to custom parsing, similarly to #241, #248. Paves the way for #285, and possibly other enhancements such as an opt-in #123. ## Solution Implement custom attribute parsing without `utils::State`. Co-authored-by: Kai Ren <tyranron@gmail.com>
Synopsis
This PR is a part of replacing all attributes having
syn::Meta
syntax to custom parsing.Solution
Replace
#[from(types(i32, "&str"))]
with#[from(i32, &str)]
and add support for deriving multi-field structs and enum variants with#[from((<tuple>), (<tuple>), ...)]
.Checklist