In a macro_rules! transcriber, nested repetitions are not handled properly. This works fine:
macro_rules! demo_macro {
($op:tt; $($arg:tt),*) {
($($op * $arg),*)
}
}
fn main() {
println!("{?:}", demo_macro!(2; 1,2,3));
}
and outputs (2, 4, 6), but this does not compile:
macro_rules! demo_macro {
([$($op:tt)*] $($arg:tt),*) {
($($($op)* $arg),*)
}
}
fn main() {
println!("{?:}", demo_macro!([2*] 1,2,3));
}
and results in this error message:
error: meta-variable `op` repeats 2 times, but `arg` repeats 3 times
--> src/main.rs:3:11
|
3 | ($($($op)* $arg),*)
| ^^^^^^^^^^^^^^
If I change 1,2,3 to 1,2 so the repetition counts match up, I get this error message:
error: attempted to repeat an expression containing no syntax variables matched as repeating at this depth
--> src/main.rs:3:13
|
3 | ($($($op)* $arg),*)
| ^^^^^
Any idea how to make demo_macro!([2*] 1,2,3) result in (2, 4, 6)?
Yay, this works!
macro_rules! unbracket {
(_ [$($tt1:tt)*]) => { $($tt1)* };
(() [$($tt1:tt)*]) => { ($($tt1)*) };
([] [$($tt1:tt)*]) => { [$($tt1)*] };
({} [$($tt1:tt)*]) => { {$($tt1)*} };
($tt0:tt [$($tt1:tt)*] #unbracket ($($tt2:tt)*) $($tt3:tt)*) => { unbracket!{ $tt0 [$($tt1)* $($tt2)*] $($tt3)*} };
($tt0:tt [$($tt1:tt)*] #unbracket [$($tt2:tt)*] $($tt3:tt)*) => { unbracket!{ $tt0 [$($tt1)* $($tt2)*] $($tt3)*} };
($tt0:tt [$($tt1:tt)*] #unbracket {$($tt2:tt)*} $($tt3:tt)*) => { unbracket!{ $tt0 [$($tt1)* $($tt2)*] $($tt3)*} };
($tt0:tt [$($tt1:tt)*] $tt2:tt $($tt3:tt)*) => { unbracket!{$tt0 [$($tt1)* $tt2] $($tt3)*} };
}
macro_rules! demo_macro {
($op:tt $($arg:tt),*) => {
unbracket!(() [] $(#unbracket $op $arg),*)
}
}
fn main() {
println!("{:?}", demo_macro!([2*] 1,2,3));
}
Related
I'm attempting to deserialize a csv value into a different type of struct based on a value passed into a function.
I am not understanding why I am receiving a mismatched types error. Why does it find struct CitiRec when it lives in a different match arm?
pub fn get_uncat_rec(path: &PathBuf, bank_type: BankType) -> Vec<UnCatRecord> {
let mut reader = csv::Reader::from_path(path).unwrap();
let mut uncat_rec: Vec<UnCatRecord> = Vec::new();
for record in reader.deserialize() {
match bank_type {
BankType::Citi => {
let rec_result: Result<CitiRec, csv::Error> = record;
match rec_result {
Ok(rec_result) => {
uncat_rec.push(rec_result.to_uncat_rec());
}
Err(err) => {
println!("Error received deserializing Citi Record: {}", err);
}
}
}
BankType::Kasaka => {
let rec_result: Result<KasakaRec, csv::Error> = record; <-- **error here**
match rec_result {
Ok(rec_result) => {
uncat_rec.push(rec_result.to_uncat_rec());
}
Err(err) => {
println!("Error received deserializing Kasaka Record: {}", err);
}
}
}
_ => {}
}
Here is the error that receive:
error[E0308]: mismatched types
--> src\normalizer.rs:26:57
|
26 | let rec_result: Result<KasakaRec, csv::Error> = record;
| ----------------------------- ^^^^^^ expected struct `KasakaRec`, found struct `CitiRec`
| |
| expected due to this
|
= note: expected enum `Result<KasakaRec, _>`
found enum `Result<CitiRec, _>`
The type cannot be conditional. record has one, and exactly one, type.
From the first match arm the compiler is concluding that you're deserializing into CitiRecs. But in the second it appears that you are deserializing into KasakaRecs, and this is a conflict.
A way to solve that is to have a separate loop for each arm, so we can deserialize to different types:
pub fn get_uncat_rec(path: &PathBuf, bank_type: BankType) -> Vec<UnCatRecord> {
let mut reader = csv::Reader::from_path(path).unwrap();
let mut uncat_rec: Vec<UnCatRecord> = Vec::new();
match bank_type {
BankType::Citi => {
for record in reader.deserialize() {
let rec_result: Result<CitiRec, csv::Error> = record;
match rec_result {
Ok(rec_result) => {
uncat_rec.push(rec_result.to_uncat_rec());
}
Err(err) => {
println!("Error received deserializing Citi Record: {}", err);
}
}
}
}
BankType::Kasaka => {
for record in reader.deserialize() {
let rec_result: Result<KasakaRec, csv::Error> = record;
match rec_result {
Ok(rec_result) => {
uncat_rec.push(rec_result.to_uncat_rec());
}
Err(err) => {
println!("Error received deserializing Kasaka Record: {}", err);
}
}
}
}
_ => {}
}
}
This question already has answers here:
How do I match based on a dynamic variable?
(4 answers)
Closed 6 months ago.
I am trying to match an enum struct. This enum has different variants with different (or none) fields, such as:
enum Enum {
One {n : usize},
Two ,
}
The match block that I am trying to achieve looks like:
match m {
Enum::One{n: special_value} => {
println!("it is special! {}", special);
}
Enum::One{n} => {
println!("not special: {}", n);
}
Enum::Two => {
println!("not special");
}
}
If I define let m = Enum::One{n: 3} and let special_value = 8 the code prints it is special! 3. This should print not special: 3.
If I change the match block to (note that I changed the variable special_value variable to the literal 8:
match m {
Enum::One { n: 8 } => {
println!("it is special!");
}
Enum::One { n } => {
println!("not special: {}", n);
}
Enum::Two => {
println!("not special");
}
}
Now defining let m = Enum::One { n: 3 } it prints as desired not special: 3.
In my actual project case, I cannot use a literal to do the match the value comes from the function call as a variable.
For this porpouse I can use this match block (note the special case has been removed and replaced by an if clause inside the general case) :
match m {
Enum::One { n } => {
if n == special_value {
println!("it is special! {}", special_value);
} else {
println!("not special: {}", n);
}
}
Enum::Two => {
println!("not special");
}
}
Is it possible to use a variable inside the match, or am I forced to use the if clause? What is the reason?
yes. you can add if statements on the match arm without creating if block inside match arm block
does this works for you?
enum Enum {
One {
n: usize
},
Two
}
fn main() {
let special_value = 123;
let m = Enum::One { n: 123 };
match m {
Enum::One { n } if n == special_value => println!("it is special! {}", special_value),
Enum::One { n } => println!("not special: {}", n),
Enum::Two => {
println!("not special");
}
}
}
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=acfaae3976e7ca9fcf096c635a4daf98
works for me.
I mean, see code
macro_rules! impls {
() => {
let a = "Hello, world!";
println!("{}", match 7 {
impls!{ 3 + 4, a }
_ => unimplemented!()
})
};
($x:expr, $name:ident) => {
($x) => $name,
}
}
fn main() {
impls!{ }
}
As I think, impls!{ 3 + 4, a } should be (3 + 4) => a, but it's not. What's wrong?
Compiler error:
error: expected one of `=>`, `if`, or `|`, found reserved identifier `_`
--> src/main.rs:6:13
|
5 | impls!{ 3 + 4, a }
| - expected one of `=>`, `if`, or `|`
6 | _ => unimplemented!()
| ^ unexpected token
...
16 | impls!{ }
| --------- in this macro invocation
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
That is not possible with declarative macros (it might be with procedural macros, I've very little experience with those so I do not know): per the documentation
Macros can expand to expressions, statements, items (including traits, impls, and foreign items), types, or patterns.
A match arm is not in and of itself a "Rust construct", it's a sub-item of a match expression (composed of a pattern, an optional guard expression and a body expression).
As a result, it can not be produced by a declarative macro.
While macros cannot expand to match arm, as said, it is still possible to use them for patterns:(playground)
struct A(usize);
struct B(f32);
macro_rules! impls {
() => {
let x = A(3 + 4);
println!("{}", match x {
impls!{ A -> 7 } => "Hello,",
_ => unimplemented!(),
})
};
($e:expr) => {
let x = B(4.0);
println!("{}", match x {
impls!{ B -> x } => if x==$e { "world!" } else {"None"},
})
};
($a:tt -> $x:tt) => { $a($x) };
}
fn main() {
impls!{ }
impls!{ 16f32.sqrt() }
impls!{ }
impls!{ 1.0 }
}
Is there any way to pattern match on an &Box<T> without using as_ref? I'm using stable Rust, so the answer can't involve box_patterns.
Essentially, I have code like this:
enum Foo {
Thing(Box<ThingKind>),
}
enum ThingKind {
One(Foo),
Two(Foo, Foo),
}
fn my_function(foo: &Foo) {
match foo {
Foo::Thing(ThingKind::One(_inner)) => { /* ... */ }
Foo::Thing(ThingKind::Two(_left, _right)) => { /* ... */ }
}
}
but the compiler doesn't like that:
error[E0308]: mismatched types
--> src/lib.rs:12:20
|
11 | match foo {
| --- this expression has type `&Foo`
12 | Foo::Thing(ThingKind::One(_inner)) => { /* ... */ }
| ^^^^^^^^^^^^^^^^^^^^^^ expected struct `Box`, found enum `ThingKind`
|
= note: expected struct `Box<ThingKind>`
found enum `ThingKind`
error[E0308]: mismatched types
--> src/lib.rs:13:20
|
11 | match foo {
| --- this expression has type `&Foo`
12 | Foo::Thing(ThingKind::One(_inner)) => { /* ... */ }
13 | Foo::Thing(ThingKind::Two(_left, _right)) => { /* ... */ }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Box`, found enum `ThingKind`
|
= note: expected struct `Box<ThingKind>`
found enum `ThingKind`
error: aborting due to 2 previous errors
Then I tried this instead:
enum Foo {
Thing(Box<ThingKind>),
}
enum ThingKind {
One(Foo),
Two(Foo, Foo),
}
fn my_function(foo: &Foo) {
match foo {
Foo::Thing(kind) => match kind {
ThingKind::One(_inner) => { /* ... */ }
ThingKind::Two(_left, _right) => { /* ... */ }
},
}
}
but the compiler gives a similar error on that one as well.
Then I tried using kind.as_ref() in the inner match, which does work, but it's kind of annoying to have to add that every time. So, is there any way to do it without as_ref?
This looks like a job for std::ops::Deref.
enum Foo {
Thing(Box<ThingKind>),
}
enum ThingKind {
One(Foo),
Two(Foo, Foo),
}
impl std::ops::Deref for Foo {
type Target = ThingKind;
fn deref(&self) -> &Self::Target {
match self {
Foo::Thing(b) => &*b,
}
}
}
fn my_function(foo: &Foo) {
match &**foo {
ThingKind::One(_inner) => { /* ... */ }
ThingKind::Two(_left, _right) => { /* ... */ }
}
}
Please help me to solve the following error:
error[E0382]: use of moved value: `nd3`
--> src/main.rs:21:21
|
19 | Node::Y { nd: nd3, max: max } => {
| --- move occurs because `nd3` has type `std::boxed::Box<Node>`, which does not implement the `Copy` trait
20 | for m in 0..max - 1 {
21 | match recur(nd3) {
| ^^^ value moved here, in previous iteration of loop
The code is as follows. Please don't mind that the code seems meaningless because I simplified it:
enum Node {
X { x: usize },
Y { nd: Box<Node>, max: usize },
Z { Nd: Vec<Box<Node>> },
}
fn recur(nd: Box<Node>) -> Result<usize, ()> {
match *nd {
/// ...
Node::Y { nd: nd3, max: max } => {
for m in 0..max - 1 {
match recur(nd3) {
Ok(y) => return Ok(y),
_ => {}
}
}
return Ok(123);
}
_ => {}
}
return Ok(0);
}
If you have an owned value (nd), and you iterate over it, and then you iterate over it again, you will get an error because it has been moved into the previous iteration of the loop.
One solution is to derive the Clone trait for Node. Clone is a common trait for the ability to explicitly duplicate an object:
#[derive(Clone)]
enum Node {
X { x: usize },
Y { nd: Box<Node>, max: usize },
Z { Nd: Vec<Box<Node>> },
}
Now you can clone nd3 as you call the function again recursively:
Node::Y { nd: nd3, max: max } => {
for m in 0..max - 1 {
match recur(nd3.clone()) {
Ok(y) => return Ok(y),
_ => {}
}
}
return Ok(123);
}
Now, you are not passing the owned value to the next iteration of the loop. Instead, you are passing an exact duplicate. Another solution would be to borrow the value. However, for smaller objects, cloning is easier.