How to pattern match on `&Box<T>` without using `as_ref` - rust

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) => { /* ... */ }
}
}

Related

rust struct fields generating by macro

I want to create a macro which on the received model generates structures with all its fields and implements trait Default, but faced such problem.
My macro struct_field! is recognized as a field, not a macro inside the model!.
Мaybe someone knows how to solve this problem
macro_rules! struct_field {
($fild_name:ident: string = $def_val:expr,) => {
pub $fild_name: Arc<RwLock<String>>
};
($fild_name:ident: string = $def_val:expr, $($fields:tt)+) => {
pub $fild_name: Arc<RwLock<String>>
struct_field!($($fields)+)
};
($fild_name:ident: bool = $def_val:expr,) => {
pub enable_kernel_settings: Arc<AtomicBool>,
};
($fild_name:ident: bool = $def_val:expr, $($fields:tt)+) => {
pub $fild_name: Arc<AtomicBool>,
struct_field!($($fields)+)
};
}
macro_rules! model {
($model:ident { $($inner_model:ident : $inner_model_name:ident { $($fields:tt)+ },)+ }) => {
pub struct $model {$(
$inner_model: $inner_model_name,
)+}
$(pub struct $inner_model_name {
struct_field!($($fields)+) // <-- error
})+
};
}
error:
error: expected `:`, found `!`
--> .../mod.rs:43:29
|
43 | struct_field!($($fields)+)
| ^ expected `:`
...
48 | / model! {
49 | | MainStruct {
50 | | inner_struct1: InnerStruct1 {
51 | | feild1: bool = true,
... |
58 | | }
59 | | }
| |_- in this macro invocation
example:
model! {
MainStruct {
inner_struct1: InnerStruct1 {
feild1: bool = true,
},
inner_struct2: InnerStruct2 {
feild1: bool = false,
feild2: bool = false,
feild3: string = "ignore",
},
}
}
expected result:
pub struct MainStruct {
inner_struct1: InnerStruct1,
inner_struct2: InnerStruct2,
}
pub struct InnerStruct1 {
feild1: Arc<AtomicBool>,
}
pub struct InnerStruct2 {
feild1: Arc<AtomicBool>,
feild2: Arc<AtomicBool>,
feild3: Arc<RwLock<String>>
}
you can test this with "cargo expand"
and you should be enabled rust-nightly
Contrary to C-like macros, Rust macros can only operate on valid AST nodes, that is, they can only be passed something that can be tokenized and, if needed, parsed to some extent, and can only "return" a valid AST node. This, in particular, rules out the invocation of a macro that would be extended as a field definition, because something like a: bool is not a valid AST node.
It is still possible to make a macro to do what you want to do, using function-style procedural macros, but it may become somehow more convoluted.

Is there a way to define `match` labels using macros?

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 }
}

Obtain a reference from a RefCell<Option<Rc<T>>> in Rust

I am having problem getting a reference out of a RefCell<Option<Rc>>.
Any suggestion?
struct Node<T> {
value: T
}
struct Consumer3<T> {
tail: RefCell<Option<Rc<Node<T>>>>,
}
impl<T> Consumer3<T> {
fn read<'s>(&'s self) -> Ref<Option<T>> {
Ref::map(self.tail.borrow(), |f| {
f.map(|s| {
let v = s.as_ref();
v.value
})
})
}
}
Gives:
error[E0308]: mismatched types
--> src/lib.rs:15:13
|
15 | / f.map(|s| {
16 | | let v = s.as_ref();
17 | | v.value
18 | | })
| |______________^ expected reference, found enum `Option`
|
= note: expected reference `&_`
found enum `Option<T>`
help: consider borrowing here
|
15 | &f.map(|s| {
16 | let v = s.as_ref();
17 | v.value
18 | })
|
error: aborting due to previous error
Playground
Mapping from one Ref to another requires that the target already exist in memory somewhere. So you can't get a Ref<Option<T>> from a RefCell<Option<Rc<Node<T>>>>, because there's no Option<T> anywhere in memory.
However, if the Option is Some, then there will be a T in memory from which you can obtain a Ref<T>; if the Option is None, obviously you can't. So returning Option<Ref<T>> may be a viable alternative for you:
use std::{cell::{Ref, RefCell}, rc::Rc};
struct Node<T> {
value: T
}
struct Consumer3<T> {
tail: RefCell<Option<Rc<Node<T>>>>,
}
impl<T> Consumer3<T> {
fn read(&self) -> Option<Ref<T>> {
let tail = self.tail.borrow();
if tail.is_some() {
Some(Ref::map(tail, |tail| {
let node = tail.as_deref().unwrap();
&node.value
}))
} else {
None
}
}
}
Playground.

Is it possible to implement this macro using only `macro_rules!`?

I'm trying to make a macro that would let me iterate through a list of types to reduce trait impl boilerplate. (I'm currently using a different macro based solution, but this seems more readable, if it's possible without adding a dependency.)
This is the syntax I am aiming for:
trait MyTrait {}
tfor! {
for Ty in [i32, u32] {
impl MyTrait for Ty {}
}
}
My attempt:
macro_rules! tfor {
(for $ty:ident in [$($typ:ident),*] $tt:tt) => {
$(
type $ty = $typ;
tfor! { #extract $tt }
)*
};
(#extract { $($tt:tt)* }) => {
$($tt)*
};
}
This generates an error as both the iterations define a type named Ty in the same scope:
|
4 | type $ty = $typ;
| ^^^^^^^^^^^^^^^^
| |
| `Ty` redefined here
| previous definition of the type `Ty` here
Playground
Is there a way around this? Can I somehow generate a random identifier in place of Ty so that this compiles, without using a proc macro or a dependency?
You can scope the trait implementation inside a const declaration. That way you can re-use the Ty name without causing conflicts.
macro_rules! tfor {
(for $ty:ident in [$($typ:ident),*] $tt:tt) => {
$(
const _: () = {
type $ty = $typ;
tfor! { #extract $tt }
};
)*
};
(#extract { $($tt:tt)* }) => {
$($tt)*
};
}
trait MyTrait {}
tfor! {
for Ty in [i32, u32] {
impl MyTrait for Ty {}
}
}

Rust macro accepting argument with a colon, a struct which is inside a module

The following code works:
pub struct Bar {
pub name: String
}
macro_rules! printme {
($myclass: ident) => {
let t = $myclass { name: "abc".to_owned() };
println!("{}", t.name);
}
}
fn main() {
printme!(Bar);
}
However, if Bar is within a module, it won't work, error is no rules expected the token :::
mod foo {
pub struct Bar {
pub name: String
}
}
macro_rules! printme {
($myclass: ident) => {
let t = $myclass { name: "abc".to_owned() };
println!("{}", t.name);
}
}
fn main() {
printme!(foo::Bar); // not allowed
}
It only works if I use an alias:
fn main() {
use foo::Bar as no_colon;
printme!(no_colon);
}
Is there a way to make it work with the colon, without the use alias?
When you write ($myclass: ident) you are saying that the user must write an identifier in that place of the macro invocation. And as you noted, Bar is an identifier, but foo::Bar is not: syntactically, this kind of list-of-identifiers-separated-by-double-colon is called a path.
You can write ($myclass: path), or if you want to limit that to existing types then you can write ($myclass: ty), as #phimuemue's answer suggested. But if you do this, it will fail when trying to use that type to build the object. That is because of how the parser work: it must parse the path and the { in the same token tree, but having the path or ty has broken the link with the {. Since this is just a parser limitation, not a semantic one, you can use a local alias as a workaround, as the other answer suggests.
However, I would suggest to use a trait based solution if possible. I consider that to me much more idiomatic:
trait Nameable {
fn new(name: &str) -> Self;
}
mod foo {
pub struct Bar {
pub name: String
}
impl super::Nameable for Bar {
fn new(name: &str) -> Bar {
Bar {
name: name.to_string()
}
}
}
}
macro_rules! printme {
($myclass: ty) => {
let t = <$myclass as Nameable>::new("abc");
println!("{}", t.name);
}
}
fn main() {
printme!( foo::Bar );
}
Or you can take out the ultimate tool of Rust macros: the list-of-token-trees, that can parse almost anything:
macro_rules! printme {
($($myclass: tt)*) => {
let t = $($myclass)* { name: "abc".to_string() };
println!("{}", t.name);
}
}
When you invoke this macro with printme!(foo::Bar) it will actually parse as a list of three token-trees: foo, :: and Bar, and then your building of the object will just work.
The drawback (or advantage) of this method is that it will eat up all your tokens, no matter what you write into the macro, and if it fails it will emit a weird error message from inside your macro, instead of saying that your token is not valid in this macro invocation.
For example, writing printme!( foo::Bar {} ) with my trait-based macro gives the most helpful error:
error: no rules expected the token `{`
--> src/main.rs:27:24
|
19 | macro_rules! printme {
| -------------------- when calling this macro
...
27 | printme!( foo::Bar {} );
| ^ no rules expected this token in macro call
While writing the same code with the token-tree-list macro produces a few not so helpful messages:
warning: expected `;`, found `{`
--> src/main.rs:21:30
|
21 | let t = $($myclass)* { name: "abc".to_string() };
| ^
...
27 | printme!( foo::Bar {} );
| ------------------------ in this macro invocation
|
= note: this was erroneously allowed and will become a hard error in a future release
error: expected type, found `"abc"`
--> src/main.rs:21:38
|
21 | let t = $($myclass)* { name: "abc".to_string() };
| - ^^^^^ expected type
| |
| tried to parse a type due to this
...
27 | printme!( foo::Bar {} );
| ------------------------ in this macro invocation
error[E0063]: missing field `name` in initializer of `foo::Bar`
--> src/main.rs:27:15
|
27 | printme!( foo::Bar {} );
| ^^^^^^^^ missing `name`
With a little trickery you can make it work:
mod foo {
pub struct Bar {
pub name: String
}
}
macro_rules! printme {
($myclass: ty) => {
type LocalT = $myclass;
let t = LocalT { name: "abc".to_owned() };
println!("{}", t.name);
}
}
fn main() {
printme!(foo::Bar);
}
accept ty (type) instead of ident (identifier)
I do not know why, but I couldn't get it working without LocalT

Resources