Сопоставить самый последний аргумент макроса Rust как замыкание

Есть ли способ настроить приведенный ниже макрос, чтобы определить, когда последний аргумент вызова макроса является замыканием? Код ниже работает, но для устранения неоднозначности используется точка с запятой, и это выглядит неправильно.

Может ли аргумент закрытия быть идентифицирован конкретно при вызове, например. closure: || { ... }?

macro_rules! invoke_me {
    ($func_name:ident, $($arg:expr),*; || $closure:expr ) => {{
        println!("{}({})", stringify!($func_name), stringify!($($arg),*));
        println!("With closure");
        let result: Result<(),String> = $closure;
        result
    }};
    ($func_name:ident, $($arg:expr),*) => {{
        println!("{}({})", stringify!($func_name), stringify!($($arg),*));
        println!("No closure");
        Ok::<(),String>(())
    }};
}

fn main() {
    invoke_me!(foo, 1, 2, 3).unwrap();
    invoke_me!(foo, 1, 2, 3; || {
        println!("Ok from closure");
        Ok(())
    }).unwrap();
    let _ = invoke_me!(foo, 1, 2, 3; || {
        println!("Error from closure");
        Err("Error".to_string())
    });
}

это может вам помочь: stackoverflow.com/questions/75203742/… вероятно, придется tt/expr munch, чтобы разделить последнее выражение, а затем сопоставить шаблон при замыкании.

ChrisB 14.08.2024 12:50
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
1
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Используем идею из Как отделить последнее выражение в повторяющихся выражениях в макросе Rust? (см. комментарий ChrisB), у меня это работает:

macro_rules! invoke_me {
    ($func_name:expr, $($rest:tt)+) => {
        invoke_me!(@transpose $func_name, [ ] $($rest)+)
    };
    (@transpose $func_name:expr, [ $($args:expr)*] || $closure:expr) => {{
        println!("{}({})", stringify!($func_name), stringify!($($args),*));
        println!("With closure");
        let result: Result<(),String> = $closure;
        result
    }};
    (@transpose $func_name:expr, [$($temp:expr)*] $arg:expr , $($rest:tt)+) => {
        invoke_me!(@transpose $func_name, [$($temp)* $arg] $($rest)+)
    };
    (@transpose $func_name:expr, [$($args:expr)*] $last_arg:expr) => {{
        println!("{}({})", stringify!($func_name), stringify!($($args),*, $last_arg));
        println!("No closure");
        Ok::<(),String>(())
    }}
}

fn main() {
    invoke_me!(foo, 1, 2, 3).unwrap();
    invoke_me!(foo, 1, 2, 3, || {
        println!("Ok from closure");
        Ok(())
    }).unwrap();
    let _ = invoke_me!(foo, 1, 2, 3, || {
        println!("Error from closure");
        Err("Error".to_string())
    });
}

детская площадка

Хотя это возможно, вероятно, было бы лучше просто оставить точку с запятой, поскольку это медленно.

Большое спасибо, что поделились этой техникой. Это действительно работает! Я предполагаю, что вы имеете в виду медленное время компиляции, поскольку макрос рекурсивный.

keldonin 14.08.2024 21:13

Другие вопросы по теме