| Crates.io | autoargs |
| lib.rs | autoargs |
| version | 0.1.1 |
| created_at | 2025-05-02 16:51:27.23508+00 |
| updated_at | 2025-05-02 16:53:02.692597+00 |
| description | A proc macro for generating argument structs with defaults |
| homepage | https://github.com/phiresky/rust-autoargs |
| repository | https://github.com/phiresky/rust-autoargs |
| max_upload_size | |
| id | 1657971 |
| size | 32,620 |
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.
When you annotate a function or method with #[autoargs], the macro:
Default for that struct, using specified default expressionsuse 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
);
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);
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)
};
}
&self, &mut self, self)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);
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>,
) { /* ... */ }
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 the default attribute from this crateThis 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};
Add to your Cargo.toml:
[dependencies]
autoargs = "0.1.0"
MIT
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".