2 releases
new 0.1.1 | May 2, 2025 |
---|---|
0.1.0 | May 2, 2025 |
#327 in Procedural macros
242 downloads per month
23KB
283 lines
autoargs
A Rust procedural macro for generating argument structs with default values, allowing for named arguments and partial argument specification. Works with both standalone functions and struct methods.
Overview
When you annotate a function or method with #[autoargs]
, the macro:
- Generates a struct to hold the function's arguments
- Implements
Default
for that struct, using specified default expressions - Creates a macro that lets you call the function with named arguments
Examples
Functions
use autoargs::{autoargs, default};
struct A(String);
struct B(u32);
struct C(bool);
fn foo() -> A { A("default_a".to_string()) }
fn bar() -> B { B(42) }
fn baz() -> C { C(true) }
#[autoargs]
fn draw(
#[default = "foo()"]
a: A,
#[default = "bar()"]
b: B,
#[default = "baz()"]
c: C,
) -> String {
format!("Drawing: a={}, b={}, c={}", a.0, b.0, c.0)
}
// Call with named arguments (unspecified arguments use defaults)
let result = draw!(
a = A("custom_a".to_string()),
b = B(100),
// c is set to its default
);
Methods
For methods, you need to use both autoargs
on the methods and impl_autoargs
on the impl block:
use autoargs::{autoargs, impl_autoargs, default};
struct Canvas {
width: u32,
height: u32,
}
impl Canvas {
fn new(width: u32, height: u32) -> Self {
Self { width, height }
}
}
#[impl_autoargs]
impl Canvas {
#[autoargs]
fn draw_rectangle(
&self,
#[default = "0"]
x: u32,
#[default = "0"]
y: u32,
#[default = "100"]
width: u32,
#[default = "50"]
height: u32,
#[default = "\"blue\".to_string()"]
color: String,
) -> String {
format!(
"Drawing a {} rectangle at ({}, {}) with size {}x{} on canvas ({}x{})",
color, x, y, width, height, self.width, self.height
)
}
#[autoargs]
fn resize(
&mut self,
#[default = "800"]
width: u32,
#[default = "600"]
height: u32,
) -> String {
self.width = width;
self.height = height;
format!("Resized canvas to {}x{}", width, height)
}
}
let canvas = Canvas::new(800, 600);
// All defaults
let result1 = draw_rectangle!(&canvas);
// Some custom values
let result2 = draw_rectangle!(&canvas, x = 10, y = 20, color = "red".to_string());
// Create and pass an args struct
let args = DrawRectangleArgs {
x: 30,
y: 40,
width: 200,
height: 100,
color: "green".to_string(),
};
let result3 = draw_rectangle!(&canvas, args);
Generated Code
For functions, the macro generates:
struct DrawArgs {
pub a: A,
pub b: B,
pub c: C,
}
impl Default for DrawArgs {
fn default() -> Self {
Self {
a: foo(),
b: bar(),
c: baz(),
}
}
}
fn draw(args: DrawArgs) -> String {
let (a, b, c) = (args.a, args.b, args.c);
// original function body
}
macro_rules! draw {
() => {
draw(DrawArgs::default())
};
($($name:ident = $value:expr),* $(,)?) => {
{
let mut args = DrawArgs::default();
$(
args.$name = $value;
)*
draw(args)
}
};
($args:expr) => {
draw($args)
};
}
For methods, the autoargs
attribute generates the args struct and modifies the method itself, while the impl_autoargs
attribute creates the macro outside the impl block (since Rust doesn't allow macro definitions inside impl blocks):
// Generated by autoargs
struct DrawRectangleArgs {
pub x: u32,
pub y: u32,
pub width: u32,
pub height: u32,
pub color: String,
}
impl Default for DrawRectangleArgs {
fn default() -> Self {
Self {
x: 0,
y: 0,
width: 100,
height: 50,
color: "blue".to_string(),
}
}
}
// Method implementation is modified by autoargs
fn draw_rectangle(&self, args: DrawRectangleArgs) -> String {
let (x, y, width, height, color) = (args.x, args.y, args.width, args.height, args.color);
// original method body
}
// Generated by impl_autoargs
macro_rules! draw_rectangle {
($self:expr) => {
$self.draw_rectangle(DrawRectangleArgs::default())
};
($self:expr, $($name:ident = $value:expr),* $(,)?) => {
{
let mut args = DrawRectangleArgs::default();
$(
args.$name = $value;
)*
$self.draw_rectangle(args)
}
};
($self:expr, $args:expr) => {
$self.draw_rectangle($args)
};
}
Features
- Named arguments with Rust-like syntax
- Default values for arguments via attributes
- Skip any argument to use its default value
- Works with functions and all method types (
&self
,&mut self
,self
) - Generates proper structs and macros with correct visibility
- Proper CamelCase naming convention for generated structs
Advanced Usage
Creating Custom Arg Structs
You can create a custom args struct and pass it directly:
let custom_args = DrawArgs {
a: custom_a,
b: DrawArgs::default().b, // Use default for b
c: custom_c,
};
// Pass the args struct directly
let result = draw!(custom_args);
Using Default Trait
If a parameter doesn't have a #[default = "..."]
attribute, it will use the type's Default
implementation:
#[autoargs]
fn simple(
// Uses String::default()
name: String,
// Uses Option::<i32>::default()
value: Option<i32>,
) { /* ... */ }
Best Practices
- Always use specific types for your parameters that implement the required traits
- Provide meaningful default values for each parameter
- Break complex functions into smaller functions with clear argument sets
- Use descriptive parameter names
Implementation Notes
Due to Rust's limitations that prevent macro definitions within impl blocks, a two-part approach is used for methods:
#[autoargs]
attribute on the method: Generates the args struct and rewrites the method to take the args struct#[impl_autoargs]
attribute on the impl block: Identifies all#[autoargs]
methods and generates macros for them outside the impl block#[default = "..."]
attribute uses thedefault
attribute from this crate
This approach allows for a clean, ergonomic usage pattern while respecting Rust's constraints.
Important: When using the library, make sure to import all the necessary components:
// For functions
use autoargs::{autoargs, default};
// For methods in impl blocks
use autoargs::{autoargs, impl_autoargs, default};
Installation
Add to your Cargo.toml:
[dependencies]
autoargs = "0.1.0"
License
MIT
Disclaimer
This crate was 100% vibe coded after my friend went on a 15-minute rant about how Rust has no default args and how all the reasons are bikeshedding or ridiculous complaints about how default args are "code smell".
Dependencies
~205–640KB
~15K SLoC