0

I'm relatively new to Rust and I'm exercising with macros.

The goal is to archive some sort if React-ish syntax within Rust lang.

I’m aware of the drastic DSL approach like jsx!(<div />), which is a good solution but of another topic. I’m seeking something in the middle, more like Flutter or SwiftUI syntax, which leverages the native language feature as much as possible, but still presents a visually hierarchical code structure.

Here I'm exploring the idea of constructing a View struct using a children! macro.

My main intension here is to enable View.children field to hold an arbitrary arity tuple. Since variadic generic tuple is not available in Rust yet, macro seems to be the only option.

See the code and comment:

struct View<T> {
  // children field holds arbitrary nullable data
  // but what i really want is Variadic Tuple, sadly not available in rust
  children: Option<T>,
}

// `children!` macro is a shorthand for Some(VariadicTuple)
// so to avoid the ugly `Some((...))` double parens syntax.
macro_rules! children {
  ($($x:expr),+ $(,)?) => (
    Some(($($x),*))
  );
}

fn main() {
  let _view = View {
    children: children!(
      42,
      "Im A String",
      View {
        children: Some(("foo", "bar")),
      },
    ),
  };
}

So far so good. Now there's this final part I really want to further optimize:

View { children: children!(...) }

Is it possible to call a macro right inside the struct body, (instead of at the value position after a field: syntax) so to eliminate the need to write children: field redundantly?

halfer
  • 19,824
  • 17
  • 99
  • 186
hackape
  • 18,643
  • 2
  • 29
  • 57
  • 1
    No, but you can let the macro generate the whole struct instantiation, including `View { }`. – Sven Marnach Aug 28 '20 at 15:52
  • Thanks Sven! I am aware of this approach. Just want to investigate other options. – hackape Aug 28 '20 at 16:58
  • Is there anything about the approach of making `view!(42, ...)` generate the whole thing that you don't like? That would be helpful to know to understand what problem you are trying to solve. – Sven Marnach Aug 28 '20 at 18:07
  • Current example I use only one `View`. But to target React-ish usage it’d be like `Window { TopBar { Content {} } }`, multiple possible structs. Map to your suggested approach, it’d be like `view!(Window, view!(TopBar, view!(Content)))`. Visually that’s not optimal syntax. – hackape Aug 28 '20 at 19:28
  • And other reason I’d like to stay closer to idiomatic rust, is that I don’t want to invalidate language support, IDE hints etc, because of heavy use of macros. – hackape Aug 28 '20 at 19:32

1 Answers1

2

Yes and no. You can not create something exactly as you describe but you can do something very very similar.

You can implement a new function for the View struct like so:

struct View<T> {
  // children field holds arbitrary Option<data>.
  children: Option<T>,
}

impl<T> View<T> {
  pub fn new() -> Self {
    Self {
      children: children!(
      42,
      "Im A String",
      View {
        children: Some(("foo", "bar")),
      },
    )
    }
  }
}

// `children!` macro is a shorthand for Some(VariadicTuple)
// so to avoid the ugly `Some((...))` double parens syntax.
macro_rules! children {
  ($($x:expr),+ $(,)?) => (
    Some(($($x),*))
  );
}

And then to create a view object you would do this:

fn main() {
  let _view1 = View::new();
  let _view2 = View::new();
  let _view3 = View::new();

  // _view1, _view2, and _view3 are *exactly* the same
}

Or if you want to add arguments, something like this with some modifications for your use case:

struct View<T> {
  // children field holds arbitrary Option<data>.
  children: Option<T>,
}

impl<T> View<T> {
  pub fn new(age: i32, string: &str) -> Self {
    Self {
      children: children!(
      age,
      string,
      View {
        children: Some(("foo", "bar")),
      },
    )
    }
  }
}

// `children!` macro is a shorthand for Some(VariadicTuple)
// so to avoid the ugly `Some((...))` double parens syntax.
macro_rules! children {
  ($($x:expr),+ $(,)?) => (
    Some(($($x),*))
  );
}

And then implementing it:

fn main() {
  let _view1 = View::new(42, "Im A String");
  let _view2 = View::new(35, "Im another string");
  let _view3 = View::new(12, "This is yet another string");

  // _view1, _view2, and _view3 are *not* the same because they have differing "age" and "string" attributes.
}

I am here to help if this is not what you are looking for or if you want more help.

Max Larsson
  • 282
  • 2
  • 10
  • Appreciate it! But not what i want. I edit OP to emphasize that i expect `children!` to accept arbitrary arity params. `new` as a function can’t do that. But then again, if `new` can be turned into a macro like `View::new!()` then it’s a solution! – hackape Aug 28 '20 at 16:52
  • Can we define a macro as a member of some struct? Like `View::new!(...)`? – hackape Aug 28 '20 at 16:57