Named arguments patchwork

Things behave in such a way that one can tinker named arguments using const generics.

const fn encode(id: &str) -> u64 {
    let a = id.as_bytes();
    let mut acc: u64 = 0;
    let mut i = 0;
    while i < a.len() {
        acc = acc.wrapping_mul(256).wrapping_add(a[i] as u64);
        i += 1;
    }
    acc
}

struct Arg<T, const N: u64>(T);

macro_rules! args {
    (fn $id:ident ($($arg:ident: $t:ty),*) $($tail:tt)*) => {
        fn $id(($(Arg($arg)),*): (
            $(Arg::<$t, {encode(stringify!($arg))}>),*)
        ) $($tail)*
    };
    ($($arg:ident = $t:expr),*) => {
        ($(Arg::<_, {encode(stringify!($arg))}>($t)),*)
    }
}

args!{fn from_polar(radius: f64, angle: f64) -> (f64, f64) {
    (radius*angle.cos(), radius*angle.sin())
}}

fn main() {
    let (x, y) = from_polar(args!{radius = 1.0, angle = 0.0});
    println!("({}, {})", x, y);
}

That's both genius and terrifying.

It doesn't seem to work when you swap the argument order, though:

error[E0308]: mismatched types
  --> src/main.rs:21:12
   |
21 |         ($(Arg::<_, {encode(stringify!($arg))}>($t)),*)
   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `125762622027123_u64`, found `418464099429_u64`
...
30 |     let (x, y) = from_polar(args!{angle = 0.0, radius = 1.0});
   |                             -------------------------------- in this macro invocation
   |
   = note: expected struct `Arg<f64, 125762622027123_u64>`
              found struct `Arg<{float}, 418464099429_u64>`
   = note: this error originates in the macro `args` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0308]: mismatched types
  --> src/main.rs:21:12
   |
21 |         ($(Arg::<_, {encode(stringify!($arg))}>($t)),*)
   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `418464099429_u64`, found `125762622027123_u64`
...
30 |     let (x, y) = from_polar(args!{angle = 0.0, radius = 1.0});
   |                             -------------------------------- in this macro invocation
   |
   = note: expected struct `Arg<f64, 418464099429_u64>`
              found struct `Arg<{float}, 125762622027123_u64>`
   = note: this error originates in the macro `args` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` due to 2 previous errors

I made an alternate version which abuses the fact that items (e.g. functions and structs) live in a different namespace to modules to create a hidden Args struct that gets passed to the function. My version won't work for references, though.

macro_rules! args {
    (fn $id:ident ($($arg:ident: $t:ty),*) $($tail:tt)*) => {
        fn $id($id::Args { $($arg),* }: $id::Args) $($tail)*

        #[doc(hidden)]
        pub mod $id {
            pub struct Args {
                $(pub $arg : $t),*
            }
        }
    };
    ($id:ident($($arg:ident = $t:expr),*)) => {
        $id($id::Args { $($arg: $t),* })
    };
}

args! {
    fn from_polar(radius: f64, angle: f64) -> (f64, f64) {
        (radius*angle.cos(), radius*angle.sin())
    }
}

fn main() {
    let (x, y) = args!(from_polar(angle = 0.0, radius = 1.0));
    println!("({}, {})", x, y);
}

(playground)

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.