22

How can I define a public struct in Rust where all the fields are public without having to repeat pub modifier in front of every field?

A pub_struct macro would be ideal:

pub_struct! Foo {
    a: i32,
    b: f64,
    // ...
}

which would be equivalent to:

pub struct Foo {
    pub a: i32,
    pub b: f64,
    //...
}
Peter Hall
  • 53,120
  • 14
  • 139
  • 204
Petrus Theron
  • 27,855
  • 36
  • 153
  • 287
  • 2
    Let's be clear: you want a struct declaration without the first column precising what's public ? A macro can probably insert those modifiers but other readers of your code will have to learn your own special syntax to understand the whole is public... – Denys Séguret Dec 20 '18 at 10:12
  • 3
    So `pub Foo { pub a: u32, pub b: u32 }` is not what you are looking for? – hellow Dec 20 '18 at 10:12
  • 1
    Imo, specifying pub modifiers are good for the understandability of the code and should not be considered as boilerplate code. But what you want can be achieved via writing your own custom `#[derive(make_struct_pub)]` keyword. – Akiner Alkan Dec 20 '18 at 10:21
  • 11
    You are solving the wrong problem. Just write `pub` in front of each field and be done with it. Repetition of code is bad because later changes to the code are error-prone – you may miss one of the repetitions. This is not an issue for writing `pub` in front of each field name. Even if you decide to change this later, there is little risk of getting it wrong. – Sven Marnach Dec 20 '18 at 10:32
  • 1
    Most of the comments so far are...unhelpful. @SvenMarnach macros exist to extend the language and keep code DRY. @Denys Seguret sorry, but I won't have you read my code - wouldn't wish it on my enemies. If you can help me write that macro, I would appreciate that. @Akiner, can you help me write a derive like that? A `pub_struct!` macro would be cool too. – Petrus Theron Dec 20 '18 at 11:01
  • 2
    @PetrusTheron, writing such a derive can be a crate sized work. To make an inspiration about the generation of such methods you can take a look at [getset crate](https://github.com/Hoverbear/getset) which is able to generate public getters and setters for you. – Akiner Alkan Dec 20 '18 at 12:00
  • 1
    @AkinerAlkan You don't need procedural macros for this – macro by example will do just fine, and it's not difficult to implement. I just think it's _wrong_ to do so, so I won't spend my time on it. – Sven Marnach Dec 20 '18 at 12:03
  • 1
    I have RSI and it hurts when I type. I'm asking to save my wrists on big public structs. @SvenMarnach if you believe your view is correct and helpful, consider posting it as an answer. I say that the comments so far are unhelpful because they assume my intent ("solving the wrong problem") or question my competence (abusing or overusing macros). Rust isn't the first language to have macros, y'know? I believe the Rust community can benefit from posting empathetic answers rather than downvoting beginner questions just because they disagree with the premise. – Petrus Theron Dec 20 '18 at 12:46
  • 5
    @PetrusTheron You can use multiple cursors or keyboard macros in your editor to easily add `pub` in front of each line, which I consider the right solution. Using macros here makes the code harder to read for people who are new to the codebase, which I believe is the reason why people are downvoting. If you are determined to use macros anyway, you can start with a rule like `(struct $name:ident { $($field:ident : $type:ty,)* }) => { pub struct $name { $(pub $field: $type,)* } }` and refine it as needed to support generic parameters and field attributes. – Sven Marnach Dec 20 '18 at 13:17
  • Thanks, @SvenMarnach :) I got it working. See answer I posted. Wish I knew how to move the derive calls out of there, unfortunately `derive may only be applied to structs, enums and unions` – Petrus Theron Dec 20 '18 at 13:32

1 Answers1

18
macro_rules! pub_struct {
    ($name:ident {$($field:ident: $t:ty,)*}) => {
        #[derive(Debug, Clone, PartialEq)] // ewww
        pub struct $name {
            $(pub $field: $t),*
        }
    }
}

Unfortunately, derive may only be applied to structs, enums and unions, so I don't know how to hoist those to the caller.

Usage:

pub_struct!(Foo {
    a: i32,
    b: f64,
});

It would be nice if I didn't need the parentheses and semicolon, i.e. if Rust supported reader macros.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Petrus Theron
  • 27,855
  • 36
  • 153
  • 287
  • 4
    [Rust's API guidelines recommend including the `struct` keyword in the macro syntax](https://rust-lang-nursery.github.io/api-guidelines/macros.html#input-syntax-is-evocative-of-the-output-c-evocative). This would also allow for [cleaner handling of the derive macros](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=61504b1b45811535371c28e8b55b7aae). – Sven Marnach Dec 20 '18 at 13:59
  • @SvenMarnach so maybe something like `pub! struct {a: i32, ...}`? – Petrus Theron Dec 20 '18 at 13:59
  • I don't think that's possible – there must be some kind of opening delimiter after the macro name. – Sven Marnach Dec 20 '18 at 14:06
  • @SvenMarnach you can remove the hardcoding of `derive`. See [Generating documentation in macros](https://stackoverflow.com/q/33999341/155423) – Shepmaster Dec 20 '18 at 16:11
  • @Shepmaster Please see the playground link in my comment above. – Sven Marnach Dec 20 '18 at 16:13
  • 1
    @SvenMarnach right... that's what I read and it hardcodes the word `derive`. I'm pointing out you don't need to do that. In addition, you expand the macro to support *any* attribute. – Shepmaster Dec 20 '18 at 16:17
  • @Shepmaster Got it – I thought you meant the hard-coded derive in the answer. Sure, the solution could be made far more generic – it could support generic type parameters, field attributes and a visibility specifier for the struct itself, but I tried to keep it simple. – Sven Marnach Dec 20 '18 at 20:20