Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

auto! Macro

The auto! macro simplifies constructing instances of types generated by derive_struct! and derive_enum!. Its primary value is resolving anonymous type names automatically — you write human-readable paths while the macro expands them to the correct generated names.

Why auto!?

When you use anonymous structs or enums, yuuka generates names like _Root_0_anonymous. Constructing these manually is verbose and fragile:

#![allow(unused)]
fn main() {
derive_struct!(Root {
    data: {
        name: String,
        score: f64,
    },
});

// Without auto! — you must know the generated name
let val = Root {
    data: _Root_0_anonymous {
        name: "test".to_string(),
        score: 99.5,
    },
};

// With auto! — just use { }
let val = auto!(Root {
    data: {
        name: "test".to_string(),
        score: 99.5,
    },
});
}

Struct Construction

Basic Struct

#![allow(unused)]
fn main() {
derive_struct!(Root {
    a: String,
    b: i32,
});

let obj = auto!(Root {
    a: "hello".to_string(),
    b: 42,
});
}

Nested Anonymous Structs

#![allow(unused)]
fn main() {
derive_struct!(Root {
    a: String,
    b: i32,
    c: f64,
    d: {
        e: String = "world".to_string(),
        f: i32,
    },
});

let obj = auto!(Root {
    a: "hello".to_string(),
    b: 42,
    c: std::f64::consts::PI,
    d: {
        f: 24,
        ..Default::default()
    },
});
assert_eq!(obj.d.e, "world"); // From default
assert_eq!(obj.d.f, 24);      // Explicitly set
}

Spread Expression

Use ..Default::default() to fill remaining fields with defaults, just like standard Rust struct update syntax:

#![allow(unused)]
fn main() {
let obj = auto!(Root {
    a: "hello".to_string(),
    b: 42,
    c: 3.14,
    d: {
        f: 24,
        ..Default::default()  // e gets its default "world"
    },
});
}

Enum Construction

Unit Variant

#![allow(unused)]
fn main() {
derive_enum!(
    #[derive(PartialEq)]
    enum Root {
        A,
        B(i32),
        C { a: String, b: i32 },
    }
);

assert_eq!(auto!(Root::A), Root::A);
}

Tuple Variant

#![allow(unused)]
fn main() {
assert_eq!(auto!(Root::B(42)), Root::B(42));
}

Struct-like Variant

#![allow(unused)]
fn main() {
assert_eq!(
    auto!(Root::C {
        a: "hello".to_string(),
        b: 42,
    }),
    Root::C {
        a: "hello".to_string(),
        b: 42,
    }
);
}

Anonymous Enum Path Resolution

This is where auto! truly shines. For anonymous enums nested inside tuple variants, auto! resolves the path through multiple levels:

Single Level

#![allow(unused)]
fn main() {
derive_enum!(
    #[derive(PartialEq)]
    enum Root {
        D(enum {
            E,
            F(i32),
            G { a: String, b: i32 },
        }),
    }
);

// Without auto! — verbose
let _ = Root::D(__Root::_Root_0_anonymous::E);

// With auto! — clean
assert_eq!(auto!(Root::D::E), Root::D(__Root::_Root_0_anonymous::E));

assert_eq!(auto!(Root::D::F(42)), Root::D(__Root::_Root_0_anonymous::F(42)));

assert_eq!(
    auto!(Root::D::G {
        a: "hello".to_string(),
        b: 42,
    }),
    Root::D(__Root::_Root_0_anonymous::G {
        a: "hello".to_string(),
        b: 42,
    })
);
}

Deeply Nested Paths

auto! can resolve paths through arbitrarily deep anonymous enum nesting:

#![allow(unused)]
fn main() {
derive_enum!(
    #[derive(PartialEq)]
    enum A {
        B(enum {
            C(enum {
                D(enum {
                    E(enum {
                        F,
                        G(String),
                    }),
                }),
            }),
        }),
    }
);

// Resolves: A::B → _A_0_anonymous::C → _A_1_anonymous::D → _A_2_anonymous::E → _A_3_anonymous::F
assert_eq!(
    auto!(A::B::C::D::E::F),
    A::B(_A_0_anonymous::C(_A_1_anonymous::D(_A_2_anonymous::E(_A_3_anonymous::F))))
);

assert_eq!(
    auto!(A::B::C::D::E::G("hello".to_string())),
    A::B(_A_0_anonymous::C(_A_1_anonymous::D(_A_2_anonymous::E(
        _A_3_anonymous::G("hello".to_string())
    ))))
);
}

Mixed Usage

You can nest auto! calls inside other auto! calls or regular struct construction:

#![allow(unused)]
fn main() {
derive_struct!(
    #[derive(PartialEq)]
    Root {
        outer: {
            a: enum B {
                C {
                    c: i32,
                    d: f64,
                },
            },
        },
    }
);

let val = auto!(Root {
    outer: {
        a: auto!(B::C { c: 42, d: std::f64::consts::PI }),
    },
});

assert_eq!(val.outer.a, B::C { c: 42, d: std::f64::consts::PI });
}

Cross-Module Usage

auto! works across module boundaries as long as the types and their helper macros are in scope:

#![allow(unused)]
fn main() {
#[macro_use]
mod definitions {
    use yuuka::derive_struct;

    derive_struct!(
        #[derive(PartialEq)]
        pub Root {
            a: String,
            b: i32,
        }
    );
}

mod usage {
    use yuuka::auto;
    use super::definitions::*;

    #[test]
    fn test() {
        assert_eq!(
            auto!(Root {
                a: "hello".to_string(),
                b: 42,
            }),
            Root {
                a: "hello".to_string(),
                b: 42,
            }
        );
    }
}
}

When using anonymous types across modules, ensure the defining module is marked with #[macro_use]:

#![allow(unused)]
fn main() {
#[macro_use]
mod definitions {
    use yuuka::derive_struct;

    derive_struct!(Root {
        data: {
            value: String,
        },
    });
}

mod usage {
    use yuuka::auto;
    use super::definitions::*;

    fn create() {
        let val = auto!(Root {
            data: {
                value: "hello".to_string(),
            },
        });
    }
}
}

For cross-crate usage, see Attributes & Visibility — Cross-Crate Usage.