0

As asked by @isaactfa,

PS C:\...> rustc --version
rustc 1.62.1 (e092d0b6b 2022-07-16)
// Cargo.toml
[dependencies]
diesel = { version = "1.4.5", features = ["postgres", "sqlite", "chrono"] }
chrono = "0.4"
libsqlite3-sys = { version = "^0", features = ["bundled"] }

Original Post

I've looked at the code more than a dozen times here: What fragment specifiers (metavariable types) should be used in macro-by-example for a module path + type name?, and I fear I'm too dense to grasp it. I tried tinkering with it myself but don't understand what's happening. I looked for examples and tutorials for Rust macros but are either too basic or too advance that half of the words are alien to me.

Like OP on that SO post, I'm using diesel. Now, how can I use as a macro!() parameter a module path? A module that is created by diesel::table!()?

The following code works:

use diesel::table;

table! {
    user (id) {
        id -> Integer,
        uuid -> Text,
        username -> Text,
    }
}

#[derive(Queryable)]
struct User {
    id: i32,
    uuid: String,
    username: String
}

macro_rules! my_macro {
    ($table:path, $username:path) => {
        impl User {
            pub fn search_w_username(
                username: String,
                conn: &DB,
            ) -> Result<Option<Self>, diesel::result::Error> {
                use crate::diesel::ExpressionMethods;
                use crate::diesel::{QueryDsl, RunQueryDsl};

                match $table
                    .filter($username.eq(username))
                    .load::<User>(conn)
                {
                    ...
                }
            }
        }
    };
}

my_macro!(user::table, user::columns::username);

But of course, the code above is a code accepted from a defeated man, and it's bugging me for more than a week now.

using the same code above, the following would fail to compile

use diesel::table;
...

macro_rules! my_macro {
    ($module_path:path) => {
        use $module_path::table;
        use $module_path::columns::username;
        ...
    }
}

Compiler error thrown:

expected one of `.`, `?`, `{`, or an operator, found `::`

As mentioned by Chayim Friedman who answered the SO post above, the error above is due to an invalid AST (another term I don't understand).

What metavariables and/or how to implement the mentioned "tt_munching", that I'm also having a hard time to understand, should be done so that the macro function signature should just be like the following:

use diesel::table

table! {
    user (id) {
        id -> Integer,
        uuid -> Text,
        username -> Text,
    }
}

macro_rules! my_macro {
    ...
}

my_macro!(user);
MikeTheSapien
  • 161
  • 3
  • 8
  • 1
    Hmm, what Rust version are you using? I can get a minimal example to compile [on the playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=07c731a4b35c8ba82faa7a1a348e7f7b). But maybe I'm just missing some subtlety around what `diesel` does. – isaactfa Aug 15 '22 at 08:19
  • 2
    Thanks for your comment, it made me wonder that perhaps it has something to do with me misunderstanding what `diesel::table` macro produces. I'll update my post. I simply changed the metavariable `$module_path:path` into `$module_path:ident` and it worked for now. I'll also include in my updated post the Rust version I'm using and also `diesel` – MikeTheSapien Aug 15 '22 at 08:44
  • Please post your answer as an answer, not as part of the question. – Sven Marnach Aug 15 '22 at 09:15
  • 1
    I'd love to add to @MikeTheSapien answer that this `use $module_path::{parse};` works while this `use $module_path::parse;` doesn't – Zhiburt Oct 10 '22 at 10:09

1 Answers1

2

The solution I came up with made me conclude that I just don't understand what diesel::table macro produces and also don't understand Rust's macro_rules!().

I simply changed the metavariable from $module_path:path into $module_path:ident and the following code snippet now works

use diesel::table;

table! {
    user (id) {
        id -> Integer,
        uuid -> Text,
        username -> Text,
    }
}

#[derive(Queryable)]
struct User {
    id: i32,
    uuid: String,
    username: String
}

macro_rules! my_macro {
($module_path:ident) => {
        impl User {
            pub fn search_w_username(
                username: String,
                conn: &DB,
            ) -> Result<Option<Self>, diesel::result::Error> {
                use crate::diesel::ExpressionMethods;
                use crate::diesel::{QueryDsl, RunQueryDsl};

                match $module_path::table
                    .filter($module_path::dsl::username.eq(username))
                    .load::<GenericUserType>(conn)
                {
                    ...
                }
            }
        }
    };
}

my_macro!(user);

Now, $module_path::table and $module_path::dsl::username works for reasons I don't understand.

Thanks to the comment, it made me rethink the metavariable type of the macro signature.

MikeTheSapien
  • 161
  • 3
  • 8
  • While Rust macros are a small improving step in the right direction (compared to C++), they are still little baby steps, while a full grown macro facility exists since decades in Common Lisp. And despite all the braces, it is still more intuitive, compared to having to learn how the AST is designed in Rust. They had better added a Lisp interpreter to the compiler and then created Rust macros as... lisp macros :) – BitTickler Aug 16 '22 at 06:00
  • Sorry, I don't follow the intent of your comment ? ¯\_(ツ)_/¯ I'm satisfied using Rust macros, I just don't happen to have given enough time studying and tinkering with it. I'm still finishing Rust in Action by McNamara and the other one by Jim Blandy I think, apart from trying to follow Rust blog posts that are too technical for me. But I do get excited building macros for production code even with lack of knowledge, ergo this basic conundrum I encounter =) – MikeTheSapien Aug 18 '22 at 14:45
  • 1
    The intent of my comment is, to keep a critical attitude towards Rust macros. It is easy to "fanboy into it", if you don't have a frame of reference. I also tinkered with Rust macros in the past, but found it all to be too "artificially scientific", comparing to the ease and simplicity and power of Common Lisp macros. So, now, unless there is no way around it, I avoid writing Rust macros and sometimes I even prefer to use Lisp to generate the "macroexpanded" Rust source code instead. Bottom line: There is room for improvement to rusts approach. Future will show. – BitTickler Aug 19 '22 at 12:30
  • Ooooohhhhhhh. Alright. That went over my head. I'll check out List macros. Thanks for the tip! – MikeTheSapien Aug 20 '22 at 11:54