Crate autodefault[][src]

Has this ever happened to you?

#[derive(Debug, Default, PartialEq, Eq)]
struct Inner {
    x: i32,
    y: i32,
    z: i32,
}

#[derive(Debug, Default, PartialEq, Eq)]
struct Mid {
    a: Inner,
    b: Inner,
    c: Inner,
    d: Inner
}

#[derive(Debug, Default, PartialEq, Eq)]
struct Outer {
    mid1: Mid,
    mid2: Mid,
    mid3: Mid,
    mid4: Mid,
}

fn build_outer() -> Outer {
    Outer {
        mid1: Mid {
            a: Inner {
                x: 10,
                ..Default::default()  // :D
            },
            b: Inner {
                y: 10,
                ..Default::default()  // :)
            },
            ..Default::default()  // :|
        },
        mid2: Mid {
            b: Inner {
                z: 10,
                ..Default::default()  // :/
            },
            ..Default::default()  // :(
        },
        ..Default::default()  // >:(
    }
}

Wouldn’t it be nice if you could omit all the tedious ..Default::default() calls when building deeply nested struct literals? Now you can! With autodefault, it’s never been easier to build up a large struct expression for your tests, bevy components, or anything else you might need!. Simply tag any function with the #[autodefault] attribute and let us handle the rest:

use autodefault::autodefault;

#[autodefault]
fn build_outer_simple() -> Outer {
    Outer {
        mid1: Mid {
            a: Inner { x: 10 },
            b: Inner { y: 10 },
        },
        mid2: Mid {
            b: Inner { z: 10 },
        }
    }
}  // :O

assert_eq!(build_outer(), build_outer_simple())

It’s never been easier!

What it’s actually doing

When applied to a function, the #[autodefault] will scan the body of the function for all struct expressions that don’t already have a ..rest trailing initializer and insert a ..Default::default(). It will do this unconditionally for all struct expressions, regardless of whether they actually implement Default, so be sure to refactor into helper functions as necessary:

use autodefault::autodefault;

struct NoDefault {
    x: i32,
    y: i32,
    z: i32,
}

#[autodefault]
fn nope() {
    let _nope = NoDefault { x: 10 };
}

Filtering Default insertions

If you only want to add ..Default::default() to some of the structs in your function, autodefault supports filtering by type name:

use autodefault::autodefault;

#[derive(Default)]
struct HasDefault {
    a: i32,
    b: i32,
    c: i32,
    d: i32,
}

struct NoDefault1 {
    a: HasDefault,
}

struct NoDefault2 {
    a: NoDefault1,
}

#[autodefault(except(NoDefault1, NoDefault2))]
fn example1() {
    let _data = NoDefault2 { a: NoDefault1 { a: HasDefault {} } };
}

#[autodefault(only(HasDefault))]
fn example2() {
    let _data = NoDefault2 { a: NoDefault1 { a: HasDefault {} } };
}

Other behaviors

autodefault will not descend into nested item definitions; if you nest an fn item inside another fn, you’ll need to tag the inner function with autodefault again.

use autodefault::autodefault;

#[derive(Default)]
struct HasDefault {
    x: i32
}

struct NoDefault {
    x: i32
}

#[autodefault]
fn outer() {
    let _x = HasDefault {};

    fn inner() {
        let _x = NoDefault {x: 10};
    }
}

Attribute Macros

autodefault

Modify a function such that some or all struct expressions include ..Default::default().