Need assistance with an example of match statement in Rust - rust

I am new to Rust and GTK and wrote a function with a long if/else if statement like so:
fn clicking_buttons(&mut self, button_name: String) {
if button_name.eq("button_one") {
self.on_btn_one();
} else if button_name.eq("button two") {
self.on_btn_two():
} else if button_name.eq("button three") {
self.on_btn_three():
} else if button_name.eq("button four") {
self.on_btn_four():
} else if button_name.eq("button five") {
self.on_btn_five():
}
}
How would I write this using that Rust match statement?

fn btn_one() {
println!("Button one")
}
fn btn_two() {
println!("Button two")
}
struct Button {
name: String,
}
fn main() {
let button = Button {
name: String::from("one"),
};
match button.name.as_str() {
"one" => btn_one(),
"two" => btn_two(),
_ => println!("Button not found!"),
}
}

Related

Error matching with downcasting the struct error types in AWS SDK

I have this code in Go:
_, err := kms.ScheduleKeyDeletion(...
ev, ok := err.(awserr.Error)
deleted := false
if ok {
switch ev.Code() {
case "KMSInvalidStateException":
deleted = strings.Contains(aerr.Message(), "pending deletion")
case "NotFoundException":
deleted = true
}
}
Basically, want to do the same in Rust using https://docs.rs/aws-sdk-kms/latest/aws_sdk_kms/client/struct.Client.html#method.schedule_key_deletion
let result = cli.schedule_key_deletion().send().await;
let success = match result {
Ok(ok_result) => { ... },
Err(e) => {
// "e" is the 'aws_sdk_kms::error::ScheduleKeyDeletionError' type
// ref. https://docs.rs/aws-sdk-kms/latest/aws_sdk_kms/error/struct.ScheduleKeyDeletionError.html
match e {
// doesn't compile...
aws_sdk_kms::error::NotFoundException => {
true
},
_ => false
}
}
}
Basically, on the return type https://docs.rs/aws-sdk-kms/latest/aws_sdk_kms/error/struct.ScheduleKeyDeletionError.html, I want to check if the return struct is https://docs.rs/aws-sdk-kms/latest/aws_sdk_kms/error/struct.NotFoundException.html or not.
#[non_exhaustive]
pub struct ScheduleKeyDeletionError {
pub kind: ScheduleKeyDeletionErrorKind,
/* private fields */
}
#[non_exhaustive]
pub struct NotFoundException {
pub message: Option<String>,
}
You can use e.kind() to get the Kind of error that occurred. The return value of e.kind will be of Enum type ScheduleKeyDeletionErrorKind. So you can match the enum variant
aws_sdk_kms::error::ScheduleKeyDeletionErrorKind::NotFoundException to see if there an NotFoundException Exception.
let result = cli.schedule_key_deletion().send().await;
let success = match result {
Ok(ok_result) => { ... },
Err(e) => {
match e.kind() {
aws_sdk_kms::error::ScheduleKeyDeletionErrorKind::NotFoundException(_)=> {
true
},
_ => false
}
}
}
Note: This solution is not tested.

How can I get all implementations of a struct and call it?

This is my dummy code that doesn't actually work, I would like to know how, or is there a way to achieve this functionality.
Currently if I want to do this, I need to define an enum and match, which is inefficient and difficult to maintain.
pub fn mainrun() {
let aimpl = MyStruct {
Name: "bar".to_string(),
};
// dummy method collector
let some_dummy = impl_method_collector(&aimpl);
for k in some_dummy {
k();
}
/*expected to get
bar say one
bar say two
bar say three
*/
}
struct MyStruct {
Name: String,
}
impl MyStruct {
fn one_fn(&self) {
println!("{:?} say one", self.Name)
}
fn two_fn(&self) {
println!("{:?} say two", self.Name)
}
fn three_fn(&self) {
println!("{:?} say three", self.Name)
}
}
Here is how I achieve the same in Go. I want to achieve something like this with Rust.
package main
import "reflect"
func main() {
println("start")
astr := &MyStruct{"bar"}
v := reflect.ValueOf(astr)
vNums := v.NumMethod()
for i := 0; i < vNums; i++ {
v.Method(i).Call([]reflect.Value{})
}
/*expected to get
start
bar say one
bar say three
bar say tow
*/
}
type MyStruct struct {
Name string
}
func (m *MyStruct) FnOne() {
println(m.Name, "say one")
}
func (m *MyStruct) FnTow() {
println(m.Name, "say tow")
}
func (m *MyStruct) FnThree() {
println(m.Name, "say three")
}
You can do something similar using a macro that defines all the "route" methods along with any "global" methods that want to use the list of "routes":
macro_rules! make_routes {
($name:ident $(fn $method:ident (&$self:ident) { $($code:tt)* })*) => {
impl $name {
$(fn $method (&$self) { $($code)* })*
// Here you define the method (or methods) that operate on the list
// of "routes".
fn call_all (&self) {
$(self.$method();)*
}
}
}
}
Then you call it like this:
struct MyStruct {
a: i32,
}
make_routes!{
MyStruct
fn route1 (&self) {
println!("Route 1");
}
fn route2 (&self) {
println!("Route 2 (a = {})", self.a);
}
}
Playground
Note that if the methods you want to call take extra parameters, then you will need to list the parameter names for each method (but you don't need to list the types since they must be the same anyway):
macro_rules! make_routes {
($name:ident $(fn $method:ident (&$self:ident, $param:pat) { $($code:tt)* })*) => {
impl $name {
$(fn $method (&$self, $param: i32) { $($code)* })*
fn call_all (&self, param: i32) {
$(self.$method (param);)*
}
}
}
}
make_routes!{
MyStruct
fn route1 (&self, param) {
println!("Route 1 (param = {})", param);
}
fn route2 (&self, param) {
println!("Route 2 (a = {}, param = {})", self.a, param);
}
}
Playground
For more details on macros, you can read The Little Book of Rust Macros.

Can I define a macro which will expand into a function call?

I've (naively) tried this, but it doesn't print anything to the screen:
macro_rules! foo {
($suffix:tt, $arg:expr) => {
concat!("foo", $suffix, "(", $arg, ")");
};
}
fn foo_i32(x: i32) {
println!("i32 {}", x);
}
fn foo_bool(x: bool) {
println!("bool {}", x);
}
fn main() {
foo!("bool", true);
foo!("i32", 1);
}
Yes, and no.
First of, concat! generates a string, so your code is essentially the same as if you wrote:
fn main() {
"foobool(true)";
"fooi32(1)";
}
which is a no-op.
To generate Rust code, the macro does not need to involve strings at all:
macro_rules! foo {
($suffix:tt, $arg:expr) => {
$suffix($arg);
};
}
which you could call as foo!(foo_bool, true);.
If however you want to construct the name foo_bool from foo and bool, you need to use concat_idents, which is currently unstable and unlikely to get stable any time soon (because it causes some hygiene issues):
#![feature(concat_idents)]
macro_rules! foo {
($suffix:tt, $arg:expr) => {
concat_idents!(foo_, $suffix)($arg);
};
}
fn foo_i32(x: i32) {
println!("i32 {}", x);
}
fn foo_bool(x: bool) {
println!("bool {}", x);
}
fn main() {
foo!(bool, true);
foo!(i32, 1);
}

How to generalise access to struct fields?

I try to find differences from two streams (represented by iterators) for later analysis, the code below works just fine, but looks a little bit ugly and error prone (copy-paste!) in updating values in update_v? functions. Is there any ways to generalise it assuming that source is matter?
struct Data {};
struct S {
v1: Option<Data>,
v2: Option<Data>
}
...
fn update_v1(diffs: &mut HashMap<u64, Data>, key: u64, data: Data) {
match diffs.entry(key) {
Entry::Vacant(v) => {
let variant = S {
v1: Some(data),
v2: None
};
v.insert(variant);
},
Entry::Occupied(e) => {
let new_variant = Some(data);
if e.get().v2 == new_variant {
e.remove();
} else {
let existing = e.into_mut();
existing.v1 = new_variant;
}
}
}
}
fn update_v2(diffs: &mut HashMap<u64, Data>, key: u64, data: Data) {
match diffs.entry(key) {
Entry::Vacant(v) => {
let variant = S {
v2: Some(data),
v1: None
};
v.insert(variant);
},
Entry::Occupied(e) => {
let new_variant = Some(data);
if e.get().v1 == new_variant {
e.remove();
} else {
let existing = e.into_mut();
existing.v2 = new_variant;
}
}
}
}
Instead of writing one function for each field, receive a pair of Fns as arguments:
fn(&S) -> Option<Data>, which can be used to replace this condition
if e.get().v1 == new_variant { /* ... */ }
with this
if getter(e.get()) == new_variant { /* ... */ }
fn(&mut S, Option<Data>) -> (), which replaces
existing.v2 = new_variant;
with
setter(&mut existing, new_variant);
Then on the call site you pass a couple lambdas like this
Getter: |d| d.v1
Setter: |s, d| s.v2 = d
Or vice-versa for the other function.
And if you want to keep the update_v1 and update_v2 function names, just write those as wrappers to this new generalized function that automatically pass the proper lambdas.
You can create a trait to facilitate different ways of accessing the structure.
trait SAccessor {
type RV;
fn new(Data) -> S;
fn v2(&S) -> &Self::RV;
fn v1_mut(&mut S) -> &mut Self::RV;
}
struct DirectSAccessor;
impl SAccessor for DirectSAccessor {
type RV = Option<Data>;
fn new(data: Data) -> S {
S {
v1: Some(data),
v2: None
}
}
fn v2(s: &S) -> &Self::RV {
&s.v2
}
fn v1_mut(s: &mut S) -> &mut Self::RV {
&mut s.v1
}
}
fn update<A>(diffs: &mut HashMap<u64, S>, key: u64, data: Data)
where A: SAccessor<RV=Option<Data>>
{
match diffs.entry(key) {
Entry::Vacant(v) => {
let variant = A::new(data);
v.insert(variant);
},
Entry::Occupied(e) => {
let new_variant = Some(data);
if A::v2(e.get()) == &new_variant {
e.remove();
} else {
let existing = e.into_mut();
*A::v1_mut(existing) = new_variant;
}
}
}
}
// ...
// update::<DirectSAccessor>( ... );
Full code

How to pattern match a String in a struct against a literal

In my code below I find that the code in match_num_works() has a certain elegance. I would like to write a String match with a similar formulation but cannot get it to work. I end up with match_text_works() which is less elegant.
struct FooNum {
number: i32,
}
// Elegant
fn match_num_works(foo_num: &FooNum) {
match foo_num {
&FooNum { number: 1 } => (),
_ => (),
}
}
struct FooText {
text: String,
}
// Clunky
fn match_text_works(foo_text: &FooText) {
match foo_text {
&FooText { ref text } => {
if text == "pattern" {
} else {
}
}
}
}
// Possible?
fn match_text_fails(foo_text: &FooText) {
match foo_text {
&FooText { text: "pattern" } => (),
_ => (),
}
}
Its probably not "elegant" or any nicer.. but one option is to move the conditional into the match expression:
match foo_text {
&FooText { ref text } if text == "pattern" => (),
_ => ()
}
Working sample: Playpen link.
Note that your desired pattern would actually work with a &str. You can't directly pattern match a String because it's a more complex value that includes an unexposed internal buffer.
struct FooText<'a> {
text: &'a str,
_other: u32,
}
fn main() {
let foo = FooText { text: "foo", _other: 5 };
match foo {
FooText { text: "foo", .. } => println!("Match!"),
_ => println!("No match"),
}
}
Playground

Resources