How to skip enum name passing its variant to a macro? - rust

I would like not to mention each time the enumname which contains the variant supposing it's always the same. Is it possible?
the key thing there is in a struct variant in the enum.
on a more mundane level:
I've tried the following:
enum Types {
t1,
t2{id: u64}
}
macro_rules! createenum {
(enum $enumname: ident { $( $variant: ident ( $arg1: literal, $arg2: expr ) ,)* } ) => {
enum $enumname {
$( $variant, )*
}
impl $enumname {
fn arg2(&self) -> Option<Types> {
match self {
$( $enumname::$variant => Some(Types::$arg2), )*
_ => None
}
}
}
}
}
createenum! {
enum MyEnum {
Var1("one", t1),
Var2("two", t2{id: 5}),
}
}
Evidently the code above does not work, since rust does not know what the t1 and t2 are. But I am loath to type the base enum each time.
How should I go about it?

use Types::*; Rust Playground demo

Related

How to disambiguate against types with Vec<T> in macro_rules in Rust?

Let's say I have
macro_rules! tipey {
(Vec<$pt: ty>) => {2};
($pt: ty) => {1};
}
macro_rules! structy {
(struct $i: ident { $($p: ident : $(Vec<)? $pt: ty $(>)?,)+ }) => {
const v: &[usize] = &[ $(tipey!($pt)),+ ];
};
}
structy!(
struct ContentDetails {
pattern: String,
fields: Vec<String>,
}
);
I want to somehow be able to disambiguate the type and know whether it is a Vec<> or a simple type. I'm only dealing with Vecs so no need to expand if not possible.
The issue I have is that if I match Vec<bool> against just $t: ty then I cannot split it up later to see if the $t was Vec<> or not but if I try to collect multiple tts or something else then parsing the list of properties breaks. I really want to avoid having to use proc macros
This is going to be very unreliable for general types, and generic Rust syntax. But if you have a very narrow use-case then you can fix up your code something like this:
macro_rules! tipey {
(Vec<$pt: tt>) => { 2 };
($pt: tt) => { 1 };
}
macro_rules! structy {
(struct $i: ident {
$($p: ident: $pt: tt $(<$gt: tt>)?),+
$(,)?
}) => {
const v: &[usize] = &[ $(tipey!( $pt $(<$gt>)?)),+ ];
};
}

Rust Macros: Repeat n times based on number of arguments without using the actual argument

Is it possible to repeat something n times based on the number of arguments without using the actual argument? My usecase would be to implement an Enum variant, that takes 1 or more type params as well as an implementation that matches the enum variant using wildcards. So the number of wildcards will depend on the number of arguments provided.
Example:
macro_rules! impl_enum {
($name: ident, $($params: ty)+, $val:expr) => {
enum MyEnum {
$name($($params)+,)
}
impl MyEnum {
fn get_val(self) {
match self {
MyEnum::$name(??????) => $val
}
}
}
}
}
Now I would like the output of
impl_enum!(Variant1, Type1 Type2 Type3, 42);
to be
enum MyEnum {
Variant1(Type1, Type2, Type3),
}
impl MyEnum {
fn get_val(self) {
match self {
MyEnum::Variant1(_,_,_) => 42,
}
}
}
Is this possible?
You can do this with a macro that converts a ty to an underscore:
macro_rules! type_as_underscore {
( $t: ty ) => { _ };
}
Now, using this and fixing up a few other errors in your macro:
macro_rules! impl_enum {
($name: ident, $($params: ty)+, $val:expr) => {
enum MyEnum {
$name($($params),+)
}
impl MyEnum {
fn get_val(self) {
match self {
MyEnum::$name($(type_as_underscore!($params)),+) => $val
};
}
}
};
}
On nightly, this is supported by the experimental feature macro_metavar_expr. You can add ${ignore(metavariable)} to a repeat expression to mark that you want it to be based on metavariable, but without actually emitting it:
#![feature(macro_metavar_expr)]
macro_rules! impl_enum {
($name: ident, $($params: ty)+, $val:expr) => {
enum MyEnum {
$name($($params),+)
}
impl MyEnum {
fn get_val(self) {
match self {
MyEnum::$name($( ${ignore(params)} _ ),*) => $val
}
}
}
}
}

Is there a way to match on all variants of an enum in rust?

Is there something that is equivalent to this:
enum ABC {
A(u32),
B(i32),
C(f64),
}
fn main() {
let abc = ABC::A(42);
match abc {
_(foo) => foo,
}
}
The reason why I'm asking is that sometimes, I want to use an enum for the different possible types, but most cases I need to handle the data in the enum variants the same exact way.
If you want to match against all possible variants and perform a common action you can
write a macro for it and reduce code repetition:
enum ABC {
A(u32),
B(i32),
C(f64),
}
#[macro_use]
macro_rules! per_letter {
($val:expr, $pattern:pat => { $res:expr }) => (
match $val {
$crate::ABC::A($pattern) => $res,
$crate::ABC::B($pattern) => $res,
$crate::ABC::C($pattern) => $res,
}
)
}
impl ABC {
pub fn print_inner(&self) {
per_letter!(self, letter => {println!("{}", letter)});
}
pub fn print_quad(&self) {
per_letter!(self, letter => {println!("{}", *letter**letter)});
}
}
Note that this is limited. You can't have multiple possible return types without wrapping them in an enum. But it's still useful if your inner types share a common functionality.
fn main() {
let foo = ABC::A(2);
foo.print_inner();
foo.print_quad();
}

Macro that wraps an arbitrary number of types

Is it possible to write a macro that defines an enum which wraps an arbitrary number of (distinct) input types?
I'd like to do a kind of type-level match.
type_switch!(i32 => println!("integer"), f32 => println!("float"), Foo => println!("foo"))
This would expand to:
{
enum Wrapper {
Variant1(i32),
Variant2(f32),
Variant3(Foo),
}
// impl From<i32>, From<f32>, From<Foo> for Wrapper
|x: Wrapper| match x {
Wrapper::Variant1(x) => println!("integer"),
Wrapper::Variant2(x) => println!("float"),
Wrapper::Variant3(x) => println!("foo"),
}
}
so that I can write like
let switch = type_switch!(i32 => println!("integer"), f32 => println!("float"), Foo => println!("foo"));
switch(32.into()); // prints "integer"
switch(24.0.into()); // prints "float"
Define a trait within your macro and implement it for each type:
macro_rules! type_switch {
($($ty: ty => $expr: expr),+) => {{
trait TypeMatch {
fn type_match(self);
}
$(
impl TypeMatch for $ty {
fn type_match(self) {
$expr
}
}
)+
TypeMatch::type_match
}}
}
Notice that the first time you call the function the compiler will bind the type so that subsequent calls must be the same type:
struct Foo;
fn main() {
let s = type_switch! {
i32 => { println!("i32"); },
f32 => { println!("f32"); },
Foo => { println!("Foo"); }
};
s(0);
s(Foo); // Error!
}
If you need to be able to call it with different types, this can be fixed (at a small cost) by using a trait object for dynamic dispatch:
macro_rules! type_switch {
($($ty: ty => $expr: expr),+) => {{
trait TypeMatch {
fn type_match(&self);
}
$(
impl TypeMatch for $ty {
fn type_match(&self) {
$expr
}
}
)+
|value: &dyn TypeMatch| {
value.type_match()
}
}}
}
struct Foo;
fn main() {
let s = type_switch! {
i32 => { println!("i32"); },
f32 => { println!("f32"); },
Foo => { println!("Foo"); }
};
s(&0);
s(&Foo);
}
Also notice that you have to pass references instead of values.
It can make sense to write wrapper types as you have proposed, but only if the type is needed in larger parts of your code.
Your specific example would define a new enum every time you use the macro, move the value into the new enum and then immediately throw it away.
That's not a idiomatic approach and if that is indeed your imagined use I'd recommend looking for different options.
That said, I have used wrapper types on a number of occasions.
Something like this will work for declaring a wrapper:
macro_rules! declare_wrapper {
(
$enum_name:ident {
$( $variant_name:ident( $typ:ty : $description:expr ) ),*
}
)=> {
pub enum $enum_name {
$(
$variant_name($typ),
)*
}
$(
impl From<$typ> for $enum_name {
fn from(value: $typ) -> Self {
$enum_name::$variant_name(value)
}
}
)*
impl $enum_name {
fn describe(&self) -> &'static str {
match self {
$(
&$enum_name::$variant_name(_) => $description,
)*
}
}
}
};
}
declare_wrapper!( MyWrapper {
MyInt(i64 : "int"),
MyString(String : "string")
});
fn main() {
let value = MyWrapper::from(22);
println!("{}", value.describe());
}
You can also extend this to add additional methods or trait impls that you need.
I've done similar things quite often.

How do I assert an enum is a specific variant if I don't care about its fields?

I'd like to check enums with fields in tests while ignoring the actual value of the fields for now.
Consider the following example:
enum MyEnum {
WithoutFields,
WithFields { field: String },
}
fn return_with_fields() -> MyEnum {
MyEnum::WithFields {
field: "some string".into(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn example() {
assert_eq!(return_with_fields(), MyEnum::WithFields {..});
}
}
playground
I'd like to use assert_eq! here, but the compiler tells me:
error: expected expression, found `}`
--> src/lib.rs:18:64
|
18 | assert_eq!(return_with_fields(), MyEnum::WithFields {..});
| ^ expected expression
This is similar to Why do I get an error when pattern matching a struct-like enum variant with fields?, but the solution does not apply in my case.
Of course, I can use match and do it myself, but being able to use assert_eq! would be less work.
Rust 1.42
You can use std::matches:
assert!(matches!(return_with_fields(), MyEnum::WithFields { .. }));
Previous versions
Your original code can be made to work with a new macro:
macro_rules! is_enum_variant {
($v:expr, $p:pat) => (
if let $p = $v { true } else { false }
);
}
#[test]
fn example() {
assert!(is_enum_variant!(return_with_fields(), MyEnum::WithoutFields {..}));
}
Personally, I tend to add methods to my enums:
fn is_with_fields(&self) -> bool {
match self {
MyEnum::WithFields { .. } => true,
_ => false,
}
}
I also tend to avoid struct-like enums and instead put in extra work:
enum MyEnum {
WithoutFields,
WithFields(WithFields),
}
struct WithFields { field: String }
impl MyEnum {
fn is_with_fields(&self) -> bool {
match self {
MyEnum::WithFields(_) => true,
_ => false,
}
}
fn as_with_fields(&self) -> Option<&WithFields> {
match self {
MyEnum::WithFields(x) => Some(x),
_ => None,
}
}
fn into_with_fields(self) -> Option<WithFields> {
match self {
MyEnum::WithFields(x) => Some(x),
_ => None,
}
}
}
I hope that some day, enum variants can be made into their own type to avoid this extra struct.
If you are using Rust 1.42 and later, see Shepmaster's answer below.
A simple solution here would be to do the opposite assertion:
assert!(return_with_fields() != MyEnum::WithoutFields);
or even more simply:
assert_ne!(return_with_fields(), MyEnum::WithoutFields);
Of course if you have more members in your enum, you'll have to add more asserts to cover all possible cases.
Alternatively, and this what OP probably had in mind, since assert! just panics in case of failure, the test can use pattern matching and call panic! directly in case something is wrong:
match return_with_fields() {
MyEnum::WithFields {..} => {},
MyEnum::WithoutFields => panic!("expected WithFields, got WithoutFields"),
}
I'd use a macro like #Shepmaster proposed, but with more error reporting (like the existing assert! and assert_eq! macros:
macro_rules! assert_variant {
($value:expr, $pattern:pat) => ({
let value = &$value;
if let $pattern = value {} else {
panic!(r#"assertion failed (value doesn't match pattern):
value: `{:?}`,
pattern: `{}`"#, value, stringify!($pattern))
}
})
// TODO: Additional patterns for trailing args, like assert and assert_eq
}
Rust playground demonstrating this example

Resources