Macro named_tup::tup

source ·
tup!() { /* proc-macro */ }
Expand description

The whole point.

Produces a named tuple, a struct that can contain a set of named arguments. Each named tuple can be added together or even default to a value if it does not already exist.

There are two different macros the tup! macro for defining an expression and the Tup! macro for defining a type.


§tup as an expression

In it’s most basic form a tup! is formed just like a struct instantiation.

let mut farm = tup!(horse: 4, chicken: 3, ants: 999_999_999);

assert_eq!(farm.horse, 4);
assert_eq!(farm.chicken, 3);

// Got some ant killer
farm.ants = 0;
assert_eq!(farm.ants, 0);

And just like it, tup! can use pre-existing variables.

let kingfisher = true;
let eagle = false;
let nest = tup!(kingfisher, toucan: true, eagle);

assert_eq!(nest, tup!(kingfisher: true, eagle: false, toucan: true))

§Tup as a type

However in certain cases defining the type exactly is necessary.

let recipe: Tup!(eggs: u8, milk: &str, flour: f32) = tup!(milk: "500ml", eggs: 4, flour: 203.6);
let person = tup!(name: "Joe", blue_eyes: true);
face_recognizer(vec![person]);

fn face_recognizer(
    people: Vec<Tup!(name: &'static str, blue_eyes: bool)>,
) -> Tup!(confidence: f64, name: &'static str) {
    tup!(confidence: 0.3, name: "Joe")
}

The type macro is also used to specify defaults using the #[tup_default] attribute and the TupInto trait to change the type.

#[tup_default]
pub fn main() {
    let result: Tup!(foo: i32 = 3, bar: Option<i32> = None) = match input {
        true => tup!(foo: 4).into_tup(),
        false => tup!(bar: Some(4)).into_tup(),
    };

    read(tup!().into_tup());
}

#[tup_default]
fn read(books: Tup!(names: &'static [&'static str] = &[], ETA: i32 = 0)) {
    // Read
}

§Tup type

Each Tup! call produces a Tup type, the type itself eagerly implements Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash assuming all the types it contains implement them. (Ord/PartialOrd is in lexicographic ordering and Ord/Eq cannot be implemented on types that use different defaults so if this is the case just convert them to non-defaulted versions before using them). As well as this a Default and Debug trait is always implemented.

assert_eq!(tup!(rooms: ["garden", "shed"]), tup!(rooms: ["garden", "shed"]));

let rooms = vec!["bathroom", "bedroom"];
let non_copy = tup!(rooms);
assert_eq!(non_copy, non_copy.clone());

let copy = tup!(cows: 4, bulls: 2);
drop(copy);
assert!(copy > tup!(cows: 5, bulls: 1));

// Will print tup { farmer: "Joe", married: true }
println!("{:?}", tup!( married: true, farmer: "Joe"));

Finally the Add trait is implemented so that you can transform between different tup types. If both sides contain a certain argument, precedence is given to the right hand side.

let farm1 = tup!(roosters: 4, dragons: 7, dogs: 1);
let farm2 = tup!(hens: 56, dogs: 3);
let combined_farm = farm1 + farm2;

assert_eq!(combined_farm, tup!(roosters: 4, hens: 56, dragons: 7, dogs: 3));