I want named arguments in Rust. Mom: We have named arguments in Rust at home:

187 points nik-rev 70 comments

Did you know that Rust has named arguments? At least you can imitate them on nightly!

This:

let opts = #[kwargs] takes_options {
    display: false,
    debug: 2,
};

The type Options is inferred, and we don't have to import it.

Is the same as this:

let opts = takes_options(Options {
    display: false,
    debug: 2,
});

With this fn and struct:

fn takes_options(opts: Options) -> Options {
    opts
}

struct Options {
    display: bool,
    debug: u32,
}

This is accomplished by defining the kwargs macro as follows:

macro_rules! kwargs {
    attr() ($fn:ident $tt:tt) => {$fn({
        type InferredType = impl ?Sized;

        if false {
            panic!() as InferredType
        } else {
            InferredType $tt
        }
    })}
}

The following is required:

  • RUSTFLAGS="-Znext-solver=globally" because the current trait solver can't deal with this code
  • #![feature(type_alias_impl_trait)] to allow type Type = impl Trait;
  • #![feature(stmt_expr_attributes)] and #![feature(proc_macro_hygiene)] to apply attribute macros on expressions

Full code:

#![feature(type_alias_impl_trait)]
#![feature(stmt_expr_attributes)]
#![feature(proc_macro_hygiene)]
#![feature(macro_attr)] // this one is optional, allows writing attribute macros with macro_rules!

macro_rules! kwargs {
    attr() ($fn:ident $tt:tt) => {$fn({
        type InferredType = impl ?Sized;

        if false {
            panic!() as InferredType
        } else {
            InferredType $tt
        }
    })}
}

fn takes_options(opts: Options) -> Options {
    opts
}

#[derive(Debug, PartialEq)]
struct Options {
    display: bool,
    debug: u32,
}

fn main() {
    let a = #[kwargs] takes_options {
        display: false,
        debug: 2,
    };

    let b = takes_options(Options {
        display: false,
        debug: 2,
    });

    assert_eq!(a, b);
}

Even more cursed

What if #![kwargs] was an attribute macro that you apply to the entire crate, and it automatically transformed any struct literal with a lowercase path?? #![feature(custom_inner_attributes)]

#![kwargs]

fn main() {
    let a = takes_options {
        display: false,
        debug: 2,
    };

    // the above is automatically transformed into this by #![kwargs]:

    let a = takes_options(Options {
        display: false,
        debug: 2,
    });

    // because the struct literal is all lowercase.
}

Don't do it

This is only for fun! Don't actually use this :)