< 返回版块

xh1109 发表于 2022-11-18 14:48

Tags:macro_rules

DRY

macro_rules! assert_equal_len {
    ($a:ident,$b:ident,$func:ident,$op:tt) => {
        assert!(
            $a.len() == $b.len(),
            "{:?}: dimension mismatch: {:?} {:?} {:?}",
            stringify!($func),
            $a.len(),
            stringify!($op),
            $b.len()
        )
    };
}

macro_rules! op {
    ($func:ident,$bound:ident,$op:tt,$method:ident) => {
        fn $func<T: $bound<T, Output = T> + Copy>(xs: &mut Vec<T>, ys: &Vec<T>) {
            assert_equal_len!(xs, ys, $func, $op);

            for (x, y) in xs.iter_mut().zip(ys.iter()) {
                *x = $bound::$method(*x, *y);
            }
        }
    };
}

op!(add_assign,Add,+=,add);
op!(mul_assign,Mul,*=,mul);
op!(sub_assign,Sub,-=,sub);
op!(div_assign,Div,-=,div);

mod test {
    use std::iter;

    macro_rules! test {
        ($func:ident,$x:expr,$y:expr,$z:expr) => {
            #[test]
            fn $func() {
                for size in 0usize..10 {
                    let mut x: Vec<_> = iter::repeat($x).take(size).collect();
                    let y: Vec<_> = iter::repeat($y).take(size).collect();
                    let z: Vec<_> = iter::repeat($z).take(size).collect();
                    super::$func(&mut x, &y);

                    assert_eq!(x, z);
                }
            }
        };
    }
    test!(add_assign, 1u32, 2u32, 3u32);
    test!(mul_assign, 2u32, 3u32, 6u32);
    test!(sub_assign, 3u32, 2u32, 1u32);
    test!(div_assign, 10, 2, 5);
}

评论区

写评论
苦瓜小仔 2022-11-18 17:28

这段代码没什么技巧,用的都是基础的声明宏知识。

小宏书 理解到 2.3.2 节就能看懂这段代码。

宏处理标记的流程:捕获 → 匹配 → 展开。

itfanr 2022-11-18 16:59

cargo expand

好命令

c5soft 2022-11-18 15:52

通过cargo expand展开宏,可以得到生成的代码:

#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
use std::ops::{Add, Div, Mul, Sub};
fn add_assign<T: Add<T, Output = T> + Copy>(xs: &mut Vec<T>, ys: &Vec<T>) {
    if !(xs.len() == ys.len()) {
        ::core::panicking::panic_fmt(
            ::core::fmt::Arguments::new_v1(
                &["", ": dimension mismatch: ", " ", " "],
                &match (&"add_assign", &xs.len(), &"+=", &ys.len()) {
                    args => {
                        [
                            ::core::fmt::ArgumentV1::new_debug(args.0),
                            ::core::fmt::ArgumentV1::new_debug(args.1),
                            ::core::fmt::ArgumentV1::new_debug(args.2),
                            ::core::fmt::ArgumentV1::new_debug(args.3),
                        ]
                    }
                },
            ),
        )
    }
    for (x, y) in xs.iter_mut().zip(ys.iter()) {
        *x = Add::add(*x, *y);
    }
}
fn mul_assign<T: Mul<T, Output = T> + Copy>(xs: &mut Vec<T>, ys: &Vec<T>) {
    if !(xs.len() == ys.len()) {
        ::core::panicking::panic_fmt(
            ::core::fmt::Arguments::new_v1(
                &["", ": dimension mismatch: ", " ", " "],
                &match (&"mul_assign", &xs.len(), &"*=", &ys.len()) {
                    args => {
                        [
                            ::core::fmt::ArgumentV1::new_debug(args.0),
                            ::core::fmt::ArgumentV1::new_debug(args.1),
                            ::core::fmt::ArgumentV1::new_debug(args.2),
                            ::core::fmt::ArgumentV1::new_debug(args.3),
                        ]
                    }
                },
            ),
        )
    }
    for (x, y) in xs.iter_mut().zip(ys.iter()) {
        *x = Mul::mul(*x, *y);
    }
}
fn sub_assign<T: Sub<T, Output = T> + Copy>(xs: &mut Vec<T>, ys: &Vec<T>) {
    if !(xs.len() == ys.len()) {
        ::core::panicking::panic_fmt(
            ::core::fmt::Arguments::new_v1(
                &["", ": dimension mismatch: ", " ", " "],
                &match (&"sub_assign", &xs.len(), &"-=", &ys.len()) {
                    args => {
                        [
                            ::core::fmt::ArgumentV1::new_debug(args.0),
                            ::core::fmt::ArgumentV1::new_debug(args.1),
                            ::core::fmt::ArgumentV1::new_debug(args.2),
                            ::core::fmt::ArgumentV1::new_debug(args.3),
                        ]
                    }
                },
            ),
        )
    }
    for (x, y) in xs.iter_mut().zip(ys.iter()) {
        *x = Sub::sub(*x, *y);
    }
}
fn div_assign<T: Div<T, Output = T> + Copy>(xs: &mut Vec<T>, ys: &Vec<T>) {
    if !(xs.len() == ys.len()) {
        ::core::panicking::panic_fmt(
            ::core::fmt::Arguments::new_v1(
                &["", ": dimension mismatch: ", " ", " "],
                &match (&"div_assign", &xs.len(), &"-=", &ys.len()) {
                    args => {
                        [
                            ::core::fmt::ArgumentV1::new_debug(args.0),
                            ::core::fmt::ArgumentV1::new_debug(args.1),
                            ::core::fmt::ArgumentV1::new_debug(args.2),
                            ::core::fmt::ArgumentV1::new_debug(args.3),
                        ]
                    }
                },
            ),
        )
    }
    for (x, y) in xs.iter_mut().zip(ys.iter()) {
        *x = Div::div(*x, *y);
    }
}
mod test {
    use std::iter;
    pub fn add_assign() {
        for size in 0usize..10 {
            let mut x: Vec<_> = iter::repeat(1u32).take(size).collect();
            let y: Vec<_> = iter::repeat(2u32).take(size).collect();
            let z: Vec<_> = iter::repeat(3u32).take(size).collect();
            super::add_assign(&mut x, &y);
            match (&x, &z) {
                (left_val, right_val) => {
                    if !(*left_val == *right_val) {
                        let kind = ::core::panicking::AssertKind::Eq;
                        ::core::panicking::assert_failed(
                            kind,
                            &*left_val,
                            &*right_val,
                            ::core::option::Option::None,
                        );
                    }
                }
            };
        }
    }
    pub fn mul_assign() {
        for size in 0usize..10 {
            let mut x: Vec<_> = iter::repeat(2u32).take(size).collect();
            let y: Vec<_> = iter::repeat(3u32).take(size).collect();
            let z: Vec<_> = iter::repeat(6u32).take(size).collect();
            super::mul_assign(&mut x, &y);
            match (&x, &z) {
                (left_val, right_val) => {
                    if !(*left_val == *right_val) {
                        let kind = ::core::panicking::AssertKind::Eq;
                        ::core::panicking::assert_failed(
                            kind,
                            &*left_val,
                            &*right_val,
                            ::core::option::Option::None,
                        );
                    }
                }
            };
        }
    }
    pub fn sub_assign() {
        for size in 0usize..10 {
            let mut x: Vec<_> = iter::repeat(3u32).take(size).collect();
            let y: Vec<_> = iter::repeat(2u32).take(size).collect();
            let z: Vec<_> = iter::repeat(1u32).take(size).collect();
            super::sub_assign(&mut x, &y);
            match (&x, &z) {
                (left_val, right_val) => {
                    if !(*left_val == *right_val) {
                        let kind = ::core::panicking::AssertKind::Eq;
                        ::core::panicking::assert_failed(
                            kind,
                            &*left_val,
                            &*right_val,
                            ::core::option::Option::None,
                        );
                    }
                }
            };
        }
    }
    pub fn div_assign() {
        for size in 0usize..10 {
            let mut x: Vec<_> = iter::repeat(10).take(size).collect();
            let y: Vec<_> = iter::repeat(2).take(size).collect();
            let z: Vec<_> = iter::repeat(5).take(size).collect();
            super::div_assign(&mut x, &y);
            match (&x, &z) {
                (left_val, right_val) => {
                    if !(*left_val == *right_val) {
                        let kind = ::core::panicking::AssertKind::Eq;
                        ::core::panicking::assert_failed(
                            kind,
                            &*left_val,
                            &*right_val,
                            ::core::option::Option::None,
                        );
                    }
                }
            };
        }
    }
}
1 共 3 条评论, 1 页