Skip to content
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

Space age #14

Merged
merged 5 commits into from
Jun 17, 2024
Merged

Space age #14

merged 5 commits into from
Jun 17, 2024

Conversation

adnilsson
Copy link
Owner

@adnilsson adnilsson commented Jun 17, 2024

Description

Solutions to the Space Age exercise.

Mentoring

First iteration

The solution in b380536 manually implements the trait, copy-paste style.
I never requested mentoring on this since it was so straightforward and there were hints to use a macro to simplify.

Second iteration

I thought: "the usual way to auto-implement traits is to use a derive-directive", and so I did in 1674489

This, more or less, requires that you use additional crates--in particular, syn--to parse the token stream into a structured syntax tree.

I realized afterward that they were looking to use a declarative macro to both define the struct and implement the trait.
However, a derive directive has its benefits. For example, you can slap a Planet implementation onto any struct you want.
Or, maybe we could rename the trait more generally as SunOrbiter so Pluto can join as well =)

I consciously added the seconds field to the Duration struct because I find it less ambiguous compared to a tuple struct. It's helpful to be specific about the intended unit. But this also made me think that an enum might be more suited: then one could choose if the duration is in seconds, days, or years.

The mentor thought I did good:

Your solution is great - I agree its much nicer than the declarative macro.

They added a suggestion that I could use a parse_meta method to improve:

    let orbital_period_meta = orbital_period_attr.parse_meta().expect("'orbital_period' is not a valid meta attribute");
    let orbital_period_expr = match orbital_period_meta {
        syn::Meta::NameValue(nv) => nv.lit,
        _ => panic!("'orbital_period' attribute must be a NameValue"),
    };

Third iteration

I thought I'd take the suggestion and see how it went.

However, the suggestion is only valid for syn 1.0, but the package is currently in 2.0 where this behavior has changed.
The replacement module syn::meta and the related method Attribute::parse_nested_meta only allow Meta::List as attributes. I'd have to redefine my helper to be #[orbit_period(years = 0.123)].

I find this strange since, to me, it seems that a MetaItem in an attribute according to the Rust reference allows SimplePath = Expression.

In any case, it might be nice to also have the unit of the orbital period in the macro, so I submitted a modified version.

The mentor did not offer any additional constructive comments.

adnilsson added 5 commits May 30, 2024 22:40
This does not use any macro to reduce boilerplate code. That's left to
the second iteration.
Uses a helper attribute to set the Planet's orbital period.

I thought to myself: "the usual way to auto-implement traits is to use a
derive-directive", and so I went on to do just that.
Although I've never written procedural macros before, this turned out to
be more involved than anticipated.
But with some grit, it was do-able.

Looking at other solutions, I realize that a declarative macro could
both create *and* implement the trait, as such:
```rust
boilerplate!(
    Mercury => 0.2408467,
    Earth => 1.0,
    ...
)
```

On the other hand, with a procedural macro you can slap a `Planet` onto
any struct you'd like! Maybe an Exoplanet struct that is also a Planet
but with extra fields, for example.
The associated constant is inspired by bobahop's solution.
I only knew of associated types, but it makes sense for associated constants to exist as well.

Also removes redundant code.
This meant I had to change the attribute's syntax to
`#[ident(key=value)]`
since a Meta::List, not a Meta::NameValue, is expected.

I find this strange since, to me, it seems that a MetaItem according to
the Rust reference should allow `SimplePath = Expression`.

In any case, it might be nice to also have the unit of the orbital
period in the macro.
@adnilsson adnilsson merged commit 5d30c5b into main Jun 17, 2024
@adnilsson adnilsson deleted the space-age branch June 17, 2024 20:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant