Given the macro matching example, this shows how macros can match an argument.
I've made very minor changes here to use numbers:
macro_rules! foo {
(0 => $e:expr) => (println!("mode X: {}", $e));
(1 => $e:expr) => (println!("mode Y: {}", $e));
}
fn main() {
foo!(1 => 3);
}
Works, printing: mode Y: 3
However I would like to use a constant as an argument, can this be made to work:
const CONST: usize = 1;
macro_rules! foo {
(0 => $e:expr) => (println!("mode X: {}", $e));
(1 => $e:expr) => (println!("mode Y: {}", $e));
}
fn main() {
foo!(CONST => 3);
}
Is this possible in Rust?
Note, using a regular match statement isn't usable for me, since in my code each branch resolves to different types, giving an error.
So I'm specifically interested to know if a constant can be passed to a macro.
No.
Macros operate on the Abstract Syntax Tree, so they reason at the syntactic level: they reason about tokens and their spelling.
For example:
fn main() {
let v = 3;
}
In this case, the AST will look something like:
fn main
\_ let-binding v
\_ literal 3
If you ask a macro whether v is 3, it will look at you funny, and wonder why you would try comparing a variable name and a literal.
I'm fairly sure the answer is "no"; at macro expansion time all you have are token trees - expansion happens before evaluation, or even type inference/checking.
const CONST: usize = 0;
macro_rules! foo {
($i:ident => $e:expr) => {
if $i == 0 {
println!("mode X: {}", $e);
} else if $i == 1 {
println!("mode Y: {}", $e);
}
};
}
fn main() {
foo!(CONST => 3);
}
If you want use identifier in macro it needs to be ident tag and you can use if, else if blocks instead of match.
Related
Given the macro matching example, this shows how macros can match an argument.
I've made very minor changes here to use numbers:
macro_rules! foo {
(0 => $e:expr) => (println!("mode X: {}", $e));
(1 => $e:expr) => (println!("mode Y: {}", $e));
}
fn main() {
foo!(1 => 3);
}
Works, printing: mode Y: 3
However I would like to use a constant as an argument, can this be made to work:
const CONST: usize = 1;
macro_rules! foo {
(0 => $e:expr) => (println!("mode X: {}", $e));
(1 => $e:expr) => (println!("mode Y: {}", $e));
}
fn main() {
foo!(CONST => 3);
}
Is this possible in Rust?
Note, using a regular match statement isn't usable for me, since in my code each branch resolves to different types, giving an error.
So I'm specifically interested to know if a constant can be passed to a macro.
No.
Macros operate on the Abstract Syntax Tree, so they reason at the syntactic level: they reason about tokens and their spelling.
For example:
fn main() {
let v = 3;
}
In this case, the AST will look something like:
fn main
\_ let-binding v
\_ literal 3
If you ask a macro whether v is 3, it will look at you funny, and wonder why you would try comparing a variable name and a literal.
I'm fairly sure the answer is "no"; at macro expansion time all you have are token trees - expansion happens before evaluation, or even type inference/checking.
const CONST: usize = 0;
macro_rules! foo {
($i:ident => $e:expr) => {
if $i == 0 {
println!("mode X: {}", $e);
} else if $i == 1 {
println!("mode Y: {}", $e);
}
};
}
fn main() {
foo!(CONST => 3);
}
If you want use identifier in macro it needs to be ident tag and you can use if, else if blocks instead of match.
I am trying to write procedural macros that will accept a Rust enum like
#[repr(u8)]
enum Ty {
A,
B
}
and generate a method for the enum that will let me convert an u8 into an allowed variant like this
fn from_byte(byte: u8) -> Ty {
match {
0 => Ty::A,
1 => Ty::B,
_ => unreachable!()
}
}
This is what I have implemented using proc_macro lib. (no external lib)
#![feature(proc_macro_diagnostic)]
#![feature(proc_macro_quote)]
extern crate proc_macro;
use proc_macro::{TokenStream, Diagnostic, Level, TokenTree, Ident, Group, Literal};
use proc_macro::quote;
fn report_error(tt: TokenTree, msg: &str) {
Diagnostic::spanned(tt.span(), Level::Error, msg).emit();
}
fn variants_from_group(group: Group) -> Vec<Ident> {
let mut iter = group.stream().into_iter();
let mut res = vec![];
while let Some(TokenTree::Ident(id)) = iter.next() {
match iter.next() {
Some(TokenTree::Punct(_)) | None => res.push(id),
Some(tt) => {
report_error(tt, "unexpected variant. Only unit variants accepted.");
return res
}
}
}
res
}
#[proc_macro_attribute]
pub fn procmac(args: TokenStream, input: TokenStream) -> TokenStream {
let _ = args;
let mut res = TokenStream::new();
res.extend(input.clone());
let mut iter = input.into_iter()
.skip_while(|tt| if let TokenTree::Punct(_) | TokenTree::Group(_) = tt {true} else {false})
.skip_while(|tt| tt.to_string() == "pub");
match iter.next() {
Some(tt # TokenTree::Ident(_)) if tt.to_string() == "enum" => (),
Some(tt) => {
report_error(tt, "unexpected token. this should be only used with enums");
return res
},
None => return res
}
match iter.next() {
Some(tt) => {
let variants = match iter.next() {
Some(TokenTree::Group(g)) => {
variants_from_group(g)
}
_ => return res
};
let mut match_arms = TokenStream::new();
for (i, v) in variants.into_iter().enumerate() {
let lhs = TokenTree::Literal(Literal::u8_suffixed(i as u8));
if i >= u8::MAX as usize {
report_error(lhs, "enum can have only u8::MAX variants");
return res
}
let rhs = TokenTree::Ident(v);
match_arms.extend(quote! {
$lhs => $tt::$rhs,
})
}
res.extend(quote!(impl $tt {
pub fn from_byte(byte: u8) -> $tt {
match byte {
$match_arms
_ => unreachable!()
}
}
}))
}
_ => ()
}
res
}
And this is how I am using it.
use helper_macros::procmac;
#[procmac]
#[derive(Debug)]
#[repr(u8)]
enum Ty {
A,
B
}
fn main() {
println!("TEST - {:?}", Ty::from_byte(0))
}
The problem is this causing an error from the compiler. The exact error being
error[E0599]: no variant or associated item named `from_byte` found for enum `Ty` in the current scope
--> main/src/main.rs:91:32
|
85 | enum Ty {
| ------- variant or associated item `from_byte` not found here
...
91 | println!("TEST - {:?}", Ty::from_byte(0))
| ^^^^^^^^^ variant or associated item not found in `Ty`
Running cargo expand though generate the proper code. And running that code directly works as expected. And so I am stumped. It could be I am missing something about how proc_macros should be used since this is the first time I am playing with them and I don't see anything that would cause this error. I am following the sorted portion of the proc_macro_workshop0. Only change is, I am using TokenStream directly instead of using syn and quote crates. Also, if I mistype the method name, the rust compiler does suggest that a method with similar name exists.
Here is a Playground repro: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=02c1ee77bcd80c68967834a53c011e41
So, indeed what you mention is true: the expanded code could be copy-pasted and it would work. When this happens (having behavior from macro expansion and "manual copy-pasted expansion" differ), there are two possibilities:
macro_rules! metavariables
When emitting code using macro_rules! special captures, some of these captures are wrapped with special invisible parenthesis that already tell the parser how the thing inside should be parsed, which make it illegal to use in other places (for instance, one may capture a $Trait:ty, and then doing impl $Trait for ... will fail (it will parse $Trait as a type, thus leading to it being interpreted as a trait object (old syntax)); see also https://github.com/danielhenrymantilla/rust-defile for other examples.
This is not your case, but it's good to keep in mind (e.g. my initial hunch was that when doing $tt::$rhs if $tt was a :path-like capture, then that could fail).
macro hygiene/transparency and Spans
Consider, for instance:
macro_rules! let_x_42 {() => (
let x = 42;
)}
let_x_42!();
let y = x;
This expands to code that, if copy-pasted, does not fail to compile.
Basically the name x that the macro uses is "tainted" to be different from any x used outside the macro body, precisely to avoid misinteractions when the macro needs to define helper stuff such as variables.
And it turns out that this is the same thing that has happened with your from_byte identifier: your code was emitting a from_byte with private hygiene / a def_site() span, which is something that normally never happens for method names when using classic macros, or classic proc-macros (i.e., when not using the unstable ::proc_macro::quote! macro). See this comment: https://github.com/rust-lang/rust/issues/54722#issuecomment-696510769
And so the from_byte identifier is being "tainted" in a way that allows Rust to make it invisible to code not belonging to that same macro expansion, such as the code in your fn main.
The solution, at this point, is easy: forge a from_bytes Identifier with an explicit non-def_site() Span (e.g., Span::call_site(), or even better: Span::mixed_site() to mimic the rules of macro_rules! macros) so as to prevent it from getting that default def_site() Span that ::proc_macro::quote! uses:
use ::proc_macro::Span;
// ...
let from_byte = TokenTree::from(Ident::new("from_byte", Span::mixed_site()));
res.extend(quote!(impl $tt {
// use an interpolated ident rather than a "hardcoded one"
// vvvvvvvvvv
pub fn $from_byte(byte: u8) -> $tt {
match byte {
$match_arms
_ => unreachable!()
}
}
}))
Playground
There are many question functions (hundreds), and each may have a different type. For each question I want to run a run_question function, which shows how long that function took, and print it's output.
I'm trying to shorten the following match expression with a Rust macro (writing run_question 100s of times does make the code rather long):
fn run_question<T: std::fmt::Display>(question_func: fn() -> T) {
let begin = Instant::now();
let output: T = question_func();
let elapsed_secs = begin.elapsed().as_micros() as f32 / 1e6;
println!("{}", output);
println!("{:.6}s taken", elapsed_secs);
}
fn q1() -> u8 { /* ... */ }
fn q2() -> u32 { /* ... */ }
fn q3() -> u64 { /* ... */ }
fn q4() -> String { /* ... */ }
fn main() {
// ...
match question_num {
1 => run_question(q1), 2 => run_question(q2), 3 => run_question(q3), 4 => run_question(q4),
_ => {
println!("Question doesn't exist.");
},
}
}
I have no experience in writing macros, and attempted the following which doesn't exactly work. It gives the error:
error: variable 'question_num' is still repeating at this depth
I'm rather stumped too how I can print the Question doesn't exist. as a default case.
#[macro_export]
macro_rules! run_questions {
( $chosen_question: expr, $( $question_num: expr, $question_mod: expr ), * ) => {
{
if $chosen_question == $question_num {
run_question($question_mod::solve);
}
}
};
}
The way I'd like to use it, is (or anything just as short is fine as well):
run_questions!(question_num, 1, q1, 2, q2, 3, q3, 4, q4);
I read a bit of the Rust book, but there aren't exactly that many examples of macros.
How would I go about doing this?
Rather than many if statements, I just reproduced the match statement
with a repetition $( ... )* for all the available branches.
It seems to behave like the extensive match expression.
macro_rules! run_questions {
( $chosen_question: expr, $( $question_num: expr, $question_mod: expr ), * ) => {
match $chosen_question {
$($question_num => run_question($question_mod),)*
_ => {
println!("Question doesn't exist.");
}
}
};
}
The error message explained:
macro_rules! run_questions {
($chosen_question: expr, $($question_num: expr, $question_mod: expr),*) => {{
In the above pattern you have a repetition with the * operator that involves variables $question_num and $question_mod
if $chosen_question == $question_num {
run_question($question_mod::solve);
}
In the corresponding code, you can't use $question_num and $question_mod directly: since they are repeated they potentially have more than one value and which one should the compiler use here? Instead, you need to tell the compiler to repeat the block of code that uses these variables. This is done by surrounding the repeated code block with $() and adding the * operator:
$(if $chosen_question == $question_num {
run_question($question_mod::solve);
})*
Although as pointed out by #prog-fh's answer, better to use a match in the macro, same as in the straight code:
match $chosen_question {
$($question_num => run_question ($question_mod::solve),)*
_ => println!("Question doesn't exist.")
};
For most operators that might overflow, Rust provides a checked version. For example, to test if an addition overflows one could use checked_add:
match 255u8.checked_add(1) {
Some(_) => println!("no overflow"),
None => println!("overflow!"),
}
This prints "overflow!". There is also a checked_shl, but according to the documentation it only checks if the shift is larger than or equal to the number of bits in self. That means that while this:
match 255u8.checked_shl(8) {
Some(val) => println!("{}", val),
None => println!("overflow!"),
}
is caught and prints "overflow!", This:
match 255u8.checked_shl(7) {
Some(val) => println!("{}", val),
None => println!("overflow!"),
}
simply prints 128, clearly not catching the overflow.
What is the correct way to check for any kind of overflow when shifting left?
I'm not aware of any idiomatic way of doing this, but something like implementing your own trait would work: Playground
The algorithm is basically to check if there are not fewer leading zeros in the number than the shift size
#![feature(bool_to_option)]
trait LossCheckedShift {
fn loss_checked_shl(self, rhs: u32) -> Option<Self>
where Self: std::marker::Sized;
}
impl LossCheckedShift for u8 {
fn loss_checked_shl(self, rhs: u32) -> Option<Self> {
(rhs <= self.leading_zeros()).then_some(self << rhs)
// in stable Rust
// if rhs <= self.leading_zeros() { Some(self << rhs) }
// else { None }
}
}
fn main() {
match 255u8.loss_checked_shl(7) {
Some(val) => println!("{}", val),
None => println!("overflow!"), // <--
}
match 127u8.loss_checked_shl(1) {
Some(val) => println!("{}", val), // <--
None => println!("overflow!"),
}
match 127u8.loss_checked_shl(2) {
Some(val) => println!("{}", val),
None => println!("overflow!"), // <--
}
}
You could do a complementary right-shift (right-shift by 8 - requested_number_of_bits) and check if 0 remains. If so, it means that no bits would be lost by left-shifting:
fn safe_shl(n: u8, shift_for: u8) -> Option<u8> {
if n >> (8 - shift_for) != 0 {
return None; // would lose some data
}
Some(n << shift_for)
}
One can also write a generic version that accepts any numeric type, including bigints (and which applied to u8 generates exactly the same code as above):
use std::mem::size_of;
use std::ops::{Shl, Shr};
fn safe_shl<T>(n: T, shift_for: u32) -> Option<T>
where
T: Default + Eq,
for<'a> &'a T: Shl<u32, Output = T> + Shr<u32, Output = T>,
{
let bits_in_t = size_of::<T>() as u32 * 8;
let zero = T::default();
if &n >> (bits_in_t - shift_for) != zero {
return None; // would lose some data
}
Some(&n << shift_for)
}
Playground
I am confused about the Some(T) keyword.
I want to check for two variables, if the value is defined (not None). If that is the case, the value of this variables is processed.
I know the match pattern which works like this:
match value {
Some(val) => println!("{}", val),
None => return false,
}
If I use this pattern, it will get very messy:
match param {
Some(par) => {
match value {
Some(val) => {
//process
},
None => return false,
}
},
None => return false,
}
This can't be the right solution.
The is a possibility, to ask if the param and value is_some() That would effect code like that:
if param.is_some() && value.is_some() {
//process
}
But if I do it like that, I always have to unwrap param and value to access the values.
I thought about something like this to avoid that. But this code does not work:
if param == Some(par) && value == Some(val) {
//process
}
The idea is that the values are accessible by par and val like they are in the match version.
Is there any solution to do something like this?
If I have several Option values to match, I match on a tuple of the values:
enum Color {
Red,
Blue,
Green,
}
fn foo(a: Option<Color>, b: Option<i32>) {
match (a, b) {
(Some(Color::Blue), Some(n)) if n > 10 => println!("Blue large number"),
(Some(Color::Red), _) => println!("Red number"),
_ => (),
}
}
fn main() {
foo(Some(Color::Blue), None);
foo(Some(Color::Blue), Some(20));
}
This allows me to match the combinations that are interesting, and discard the rest (or return false, if that is what you want to do).
If your function is processing multiple Option values, and would like to discard them if they're not Some, your function could return an Option itself:
fn foo(param: Option<usize>, value: Option<usize>) -> Option<usize> {
let result = param? + value?;
Some(result)
}
This will short-circuit the function in case there's a None value stored in either param or value.
Please read the book for more information on the ? operator.
If your function can't return an Option, you can still get away with destructuring using if let or match:
let x = if let (Some(p), Some(v)) = (param, value) {
p + v
} else {
return 0;
}
let x = match (param, value) {
(Some(p), Some(v)) => p + v,
(Some(p), _) => p,
(_, Some(v) => v,
_ => return 0,
}
Please read What is this question mark operator about? for more information on the ? operator
Please read this chapter in Rust by Example for more information on destructuring multiple things at once
There's a couple more alternatives not yet listed:
If you're willing to use experimental features (and hence the nightly compiler) you can use a try block as an alternative of extracting a function.
#![feature(try_blocks)]
fn main() {
let par: Option<f32> = Some(1.0f32);
let value: Option<f32> = Some(2.0f32);
let x: Option<f32> = try { par? + value? };
println!("{:?}", x);
}
Another alternative is to use map which only applies if the value is not None
let x: Option<f32> = par.map(|p| value.map(|v| p + v));