Is it possible to match structs to compare them? - rust

Similar to How to match struct fields in Rust?, is it possible to match a struct like Default without physically writing out the fields? I do not want to write out the fields constantly.
Something along the lines of:
let someValue = Struct { /* ... */ };
match someValue {
Struct::default() => println!("Default!"),
_ => println!("Not Default"),
}
This gives an error.
I did some testing on the Rust Playground but I only ended up running into the problem of matching named variables described in the docs.
What is your best solution to comparing many structs? Is it using #[derive(PartialEq)] and if statements?

Rust's patterns aren't values to compare to. They're more related to variable assignment (destructuring).
There is a "match guard" syntax that can be used:
match some_value {
tmp if tmp == Struct::default() => /* it's default-like-ish */
}

Related

Automatically create enum variants

I want macros that automatically add structures with attribute macros to the enum.
For example
#[add_enum(AutoEnum)]
struct A(usize);
#[add_enum(AutoEnum)]
struct B(i32);
#[auto_enum]
enum AutoEnum{}
Expand on the above
struct A(usize);
struct B(i32);
enum AutoEnum{
A(A),
B(B),
}
I have looked around and could not find a way to get information on other attribute macros (e.g. #[add_enum(AutoEnum)] from #[auto_enum]).
Is this possible?
Here's a crazy idea that will probably don't work in your real code but should work in the example you gave (after a slight modification) so 🤷.
As opposed to every other item in Rust, macros can shadow each other. If you will have two macros with the same name, the macro defined later will shadow the first macro.
So, I think... What if we will define macros for all possible names of the enum variant, then shadow them for each actual enum? This will give us a way to expand every variant to what we want.
It will be clearer with an example. Suppose we want to only support letters A-C as variant names, and only support one letter (no AC). We first need some place to define the "default" no-variant macros, and it needs to be before any variant. So let's modify the example as follows:
#[auto_enum(declare)] // New!
enum AutoEnum {}
#[add_enum(AutoEnum)]
struct A(usize);
#[add_enum(AutoEnum)]
struct B(i32);
#[auto_enum(define)] // Changed! (This is not necessary).
enum AutoEnum {}
Then, the #[auto_enum(declare)] will expand to the following three "default" macros, that pass their input as-is to the next macro except the last macro that creates the final enum:
macro_rules! AutoEnum_A {
( $($t:tt)* ) => { AutoEnum_B! { $($t)* } };
}
macro_rules! AutoEnum_B {
( $($t:tt)* ) => { AutoEnum_C! { $($t)* } };
}
macro_rules! AutoEnum_C {
( $($t:tt)* ) => {
enum AutoEnum { $($t)* }
};
}
Now, every #[add_enum] call will expand to a macro that shadows the "default" macro, and instead of passing the input to the next macro as-is, it adds its variant to it:
// #[add_enum(AutoEnum)]
// struct A(usize);
macro_rules! AutoEnum_A {
( $($t:tt)* ) => { AutoEnum_B! { $($t)* A(usize), } };
}
// #[add_enum(AutoEnum)]
// struct B(i32);
macro_rules! AutoEnum_B {
( $($t:tt)* ) => { AutoEnum_C! { $($t)* B(i32), } };
}
Finally, the #[auto_enum(define)] will expand to a call to the first macro, a call that eventually at the end of the chain will generate the enum:
AutoEnum_A! {}
Of course, for every letter (assuming you want to support uppercase/lowercase/numbers/underscores) this requires 63 combinations, which means that even supporting 5 letters would require 992436543 macros, so this is not really usable. But still, an interesting idea to explore.
I'm still seeking better ideas that are hopefully also usable.

How to use the patterns against enum with a default case with param

I have this enum
enum Foo {
MyFoo(String),
YourFoo(String),
HisFoo(String),
TheirFoo(Vec<String>),
}
I would like to apply the same logic to the first 3 variants and a special one to the last one. So I'm trying to do something like this:
match foo {
Foo::TheirFoo(s_vec) => // do something using the vector of strings
_(s) => // do something else using the string
}
Is there a way to accomplish it without specifying every single case?
Use or-patterns:
match foo {
Foo::TheirFoo(s_vec) => { /* do something using the vector of strings */ }
Foo::MyFoo(s) | Foo::YourFoo(s) | Foo::HisFoo(s) => { /* do something else using the string */ }
}
This only works because all three variants bind the same variables with the same types (s: String).

How to match an argument with dots in Rust macros?

I am writing a program and it contains a lot of matchblocks as I keep calling methods and functions that return Result struct type results.
So I was thinking maybe a macro will reduce the amount of code.
And the final macro is like this:
#[macro_export]
macro_rules! ok_or_return {
//when calls on methods
($self: ident, $method: ident($($args: tt)*), $Error: ident::$err: ident) => {{
match $self.$method($($args)*) {
Ok(v) => v,
Err(e) => {
dbg!(e);
return Err($Error::$err);
}
}
}};
}
As yo can see, I use $($args: tt)* to match multiple arguments, and it goes pretty well. Even when I use struct.method() as a form of argument, it compiled.
Like:
ok_or_return!(self, meth(node.get_num()), Error::GetNumError);
However, if I use the same form to match a normal macro argument, it failed. I changed the specifier to tt, and it didn't work out. Like:
ok_or_return!(self.people, meth(node.get_num()), Error::GetNumError);
So my problem is why node.get_num() can be matched and self.people can't?

How to get the byte representation of an IP address from a SockAddr in Rust?

A call to the function recv_from() returns a (length, SocketAddr) tuple. There is no method on this enum to extract an IP address as a byte array, however it does contain a IpAddr enum which can be obtained with the ip() function. The IpAddr contains an Ipv4Addr or Ipv6Addr struct, which have a function called octets() that does exactly what I need. Is it possible to get access to this from a SocketAddr?
Yes, you can, but ultimately you need to account for the fact that an address could be v4 or v6, and handle accordingly, using pattern matching. Basically:
match sa.ip() {
IpAddr::V4(ip) => /* something with ip.octets() which is a [u8; 4] */,
IpAddr::V6(ip) => /* something with ip.octets() which is a [u8; 16] */,
}
for example, if you wanted to be future-unfriendly, you could
let octets = match sa.ip() {
IpAddr::V4(ip) => Ok(ip.octets()),
_ => Err("Not an IPv4 Address"),
};
which returns a successful Result for v4 addresses, but errors on v6 ones. I don't really know Rust, but it looks like the only really right way to handle the type disparity between the return values of octets() in the two cases is either to wrap the result in your own enum, or else just avoid it by only working with the octets inside of an appropriate pattern match.
The solution that I ended up using looks like this:
let ip_bytes = match addr.ip() {
IpAddr::V4(ip) => ip.octets().to_vec(),
IpAddr::V6(ip) => ip.octets().to_vec(),
};

Is it possible to combine two patterns, one with a match guard, in the same match arm?

I want to check if a string contains '$' and if there is something after the '$':
I tried this code:
fn test(s: String) {
match s.find('$') {
None | (Some(pos) if pos == s.len() - 1) => {
expr1();
}
_ => { expr2(); }
}
}
But it doesn't compile:
error: expected one of `)` or `,`, found `if`
Is it impossible to combine None and Some in one match-arm?
If so, is there a simple way to not duplicate expr1() except moving it into a separate function?
It is impossible to have the match-guard (the if thingy) apply to only one pattern alternative (the things separated by | symbols). There is only one match-guard per arm and it applies to all patterns of that arm.
However, there are many solutions for your specific problem. For example:
if s.find('$').map(|i| i != s.len() - 1).unwrap_or(false) {
expr2();
} else {
expr1();
}

Resources