Есть ли способ подсчета с помощью макросов?
Я хочу создать макрос, который печатает «Hello» определенное количество раз. Он используется как:
many_greetings!(3); // expands to three `println!("Hello");` statements
Наивный способ создания этого макроса:
macro_rules! many_greetings { ($times:expr) => {{ println!("Hello"); many_greetings!($times - 1); }}; (0) => (); }
Однако это не работает, потому что компилятор не оценивает выражения; $times - 1
не вычисляется, а вводится как новое выражение в макрос.
- Когда реализуются блокировки Fn, FnMut и FnOnce?
- Как отключить предупреждения о неиспользуемом коде в Rust?
- Что такое нелексические времена жизни?
- Как искать и вставлять в HashMap эффективно?
- Почему нужны явные времена жизни в Rust?
- Почему моя переменная не живет достаточно долго?
- Пожизненные проблемы, связанные ссылками между streamами
- Каковы варианты прекращения изменчивого заимствования в Rust?
- Ячейка OpenSSL не скомпилирована в Mac OS X 10.11
- Что делает что-то «признаком объекта»?
- Ошибка «ожидаемый тип параметра» в конструкторе общей структуры
- Как вернуть ссылку на что-то внутри RefCell без нарушения инкапсуляции?
- не может считать `self.x` неизменным, потому что` * self` также заимствован как изменяемый
В то время как обычная макросистема не позволяет вам многократно повторять расширение макроса, нет проблем с использованием цикла for в макросе:
macro_rules! many_greetings { ($times:expr) => {{ for _ in 0..$times { println!("Hello"); } }}; }
Если вам действительно нужно повторить макрос, вам нужно изучить процедурные macros / компиляторы (которые по состоянию на 1.4 нестабильны и немного сложнее писать).
Редактирование: Вероятно, есть более эффективные способы реализации этого, но я потратил достаточно долго на это на сегодня, так что здесь. repeat!
, макрос, который на самом деле дублирует блок кода несколько раз:
main.rs
#![feature(plugin)] #![plugin(repeat)] fn main() { let mut n = 0; repeat!{ 4 { println!("hello {}", n); n += 1; }}; }
lib.rs
#![feature(plugin_registrar, rustc_private)] extern crate syntax; extern crate rustc; use syntax::codemap::Span; use syntax::ast::TokenTree; use syntax::ext::base::{ExtCtxt, MacResult, MacEager, DummyResult}; use rustc::plugin::Registry; use syntax::util::small_vector::SmallVector; use syntax::ast::Lit_; use std::error::Error; fn expand_repeat(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box { let mut parser = cx.new_parser_from_tts(tts); let times = match parser.parse_lit() { Ok(lit) => match lit.node { Lit_::LitInt(n, _) => n, _ => { cx.span_err(lit.span, "Expected literal integer"); return DummyResult::any(sp); } }, Err(e) => { cx.span_err(sp, e.description()); return DummyResult::any(sp); } }; let res = parser.parse_block(); match res { Ok(block) => { let mut stmts = SmallVector::many(block.stmts.clone()); for _ in 1..times { let rep_stmts = SmallVector::many(block.stmts.clone()); stmts.push_all(rep_stmts); } MacEager::stmts(stmts) } Err(e) => { cx.span_err(sp, e.description()); DummyResult::any(sp) } } } #[plugin_registrar] pub fn plugin_registrar(reg: &mut Registry) { reg.register_macro("repeat", expand_repeat); }
добавлено в Cargo.toml
[lib] name = "repeat" plugin = true
Обратите внимание: если мы действительно не хотим делать цикл, но расширяемся во время компиляции, мы должны делать что-то вроде требующих буквальных чисел. В конце концов, мы не можем оценивать переменные и вызовы функций, которые ссылаются на другие части программы во время компиляции.
Насколько я знаю, нет. Язык макросов основан на сопоставлении шаблонов и подстановке переменных и оценивает только macros.
Теперь вы можете выполнить подсчет с оценкой: это просто скучно … см. Манеж
macro_rules! many_greetings { (3) => {{ println!("Hello"); many_greetings!(2); }}; (2) => {{ println!("Hello"); many_greetings!(1); }}; (1) => {{ println!("Hello"); many_greetings!(0); }}; (0) => (); }
Исходя из этого, я вполне уверен, что можно было бы придумать набор макросов для «подсчета» и вызвать различные операции на каждом шаге (со счетом).