How to avoid redundant function calls when pattern matching? - rust

I want to match several function results without calling all of them if it is not necessary:
fn foo() -> bool {
println!("foo executed");
true
}
// I want to do something like this
// but there is a redundant function call
match (foo(), foo()) {
(false, true) => println!("Bingo!"),
_ => println!("Wrong result"),
}
// No redundant function call
// but less impressive and doubling of wrong result processing
match foo() {
false => match foo() {
true => println!("Bingo"),
_ => println!("Wrong result"),
},
_ => println!("Wrong result"),
}
How can I do this?

You can simply do:
if !foo() && foo() { println!("Bingo") } else { println!("Wrong result") }
The "and" (&&) and "or" (||) logical operators in Rust are short-circuiting, like in most languages.
Since !foo() is false, the right side of the && will not be evaluated and foo() will not be called a second time.
Your macro solution is basically re-inventing short-circuiting, at least for this toy example (maybe it becomes more readable with your actual code...).

I found out that I could beautify the second approach with macro:
macro_rules! lazy_match {
(($condition:expr => $expect:pat) => $on_expected:expr, _ => $other:expr) => (
match $condition {
$expect => $on_expected,
_ => $other,
}
);
(
($condition:expr => $expect:pat, $($c:expr => $e:pat),+)
=> $on_expected:expr, _ => $other:expr
) => (
match $condition {
$expect => lazy_match!(($($c => $e),+) => $on_expected, _ => $other),
_ => $other,
}
);
}
lazy_match! {
(foo() => false, foo() => true) => println!("Bingo"),
_ => println!("Wrong result")
};

Related

Where will String::from("") be allocated in a match arm?

I am still very new to rust, coming from a C embedded world.
If i have a piece of code like this:
match self {
Command::AT => String::from("AT"),
Command::GetManufacturerId => String::from("AT+CGMI"),
Command::GetModelId => String::from("AT+CGMM"),
Command::GetFWVersion => String::from("AT+CGMR"),
Command::GetSerialNum => String::from("AT+CGSN"),
Command::GetId => String::from("ATI9"),
Command::SetGreetingText { ref enable, ref text } => {
if *enable {
if text.len() > 49 {
// TODO: Error!
}
write!(buffer, "AT+CSGT={},{}", *enable as u8, text).unwrap();
} else {
write!(buffer, "AT+CSGT={}", *enable as u8).unwrap();
}
buffer
},
Command::GetGreetingText => String::from("AT+CSGT?"),
Command::Store => String::from("AT&W0"),
Command::ResetDefault => String::from("ATZ0"),
Command::ResetFactory => String::from("AT+UFACTORY"),
Command::SetDTR { ref value } => {
write!(buffer, "AT&D{}", *value as u8).unwrap();
buffer
},
Command::SetDSR { ref value } => {
write!(buffer, "AT&S{}", *value as u8).unwrap();
buffer
},
Command::SetEcho { ref enable } => {
write!(buffer, "ATE{}", *enable as u8).unwrap();
buffer
},
Command::GetEcho => String::from("ATE?"),
Command::SetEscape { ref esc_char } => {
write!(buffer, "ATS2={}", esc_char).unwrap();
buffer
},
Command::GetEscape => String::from("ATS2?"),
Command::SetTermination { ref line_term } => {
write!(buffer, "ATS3={}", line_term).unwrap();
buffer
}
}
How does it work in Rust? Will all these match arms evaluate immediately, or will only the one matching create a mutable copy on the stack? And also, will all the string literals withing my String::from("") be allocated in .rodata?
Is there a better way of doing what i am trying to do here? Essentially i want to return a string literal, with replaced parameters (the write! macro bits)?
Best regards
Only the matching arm will be evaluated. The non matching arms have no cost apart the size of the program.
In the general case, it's not even possible to evaluate other arms, as they depend on data read using destructuring of the pattern.
As for your second question, the location in a program where literals are stored isn't commonly named rodata, and it's neither specified nor guaranteed (it's usually deduplicated but that's just optimization).

Is it possible to use reduce more than the array length?

It's the first time I use reduce with promise, and it's look perfect for my usage.
However, I'm trying to use reduce to iterate more than the original size of my array (call reduce_array in my example).
In fact, in this example, methodThatReturnsAPromise worth true in certain case, false in other. (in the end it always finish by worth false)
The idea is if results is false then reduce work normaly, and go to the next value (nextId). In the other hand, if results is true,
I have to resolve methodThatReturnsAPromise with the same value again.
I already try differents methods using the index in parameter, or I already try to push again the id in reduce_array but none of that work.
reduce_array.reduce((accumulatorPromise, nextId, index, array) => {
return accumulatorPromise.then((results) => {
//if results === end || result === unknownDomain
//next
//first iteration, results don't exist
if (results) {
if (results === true) {
index++;
return methodThatReturnsAPromise(nextId - 1);
} else {
return methodThatReturnsAPromise(nextId);
}
} else {
return methodThatReturnsAPromise(nextId);
}
})
}, Promise.resolve());
A do/while loop inside a for loop will probably be a lot simpler:
(async () => {
for (const nextId of reduce_array) {
let result;
do {
result = await methodThatReturnsAPromise(nextId);
} while (result !== true);
}
})();
If you had to use reduce, you could put a named function which calls itself recursively inside the reduce callback:
reduce_array.reduce((accumulatorPromise, nextId) => {
return accumulatorPromise.then(() => {
const getThisInfo = () => methodThatReturnsAPromise(nextId)
.then((result) => (
result === true
? getThisInfo()
: null
));
return getThisInfo();
})
}, Promise.resolve());
Which is a bit ugly and not so easy to read. I'd prefer the for loop.

How to use metaprogramming to create an enum in Perl 6?

Given data that represents an enum, such as:
my %enums := {
Color => { red => 0, black => 1, green => 2 },
Status => { fail => 0, pass => 1 }
};
How can I use Metamodel::ClassHOW to create enums equivalent to:
enum Color ( red => 0, black => 1, green => 2 );
enum Status ( fail => 0, pass => 1 );
Timo's ADT library gives an example of how to create a class with ClassHOW, but it doesn't cover enums: https://github.com/timo/ADT/blob/master/lib/ADT.pm6
This seems to do the trick, but it's mostly untested:
my %enums := {
Color => { red => 0, black => 1, green => 2 },
Status => { fail => 0, pass => 1 }
};
my #types = gather {
for %enums.kv -> $name, %values {
my $type = Metamodel::EnumHOW.new_type(:$name, base_type => Int);
for %values -> $pair {
$type.^add_enum_value($pair);
}
$type.^add_role(Enumeration);
$type.^add_role(NumericEnumeration);
$type.^compose;
take $type;
}
}.list;
say #types; # Output: [(Status) (Color)]
Note that this puts the types into a data structure, because lexical scopes are immutable at run time, so you can't declare them just as you would with enum Color ....

Evaluate field as expression in Logstash filter

I have one custom field in Logstash event defined as expression:
{ "customIndex" => "my-service-%{+YYYY.MM}" }
And filter that calculates index name for elasticsearch output plugin:
filter {
if [customIndex] {
mutate {
add_field => { "indexName" => "custom-%{customIndex}" }
}
} else {
mutate {
add_field => { "indexName" => "common-%{+YYYY.MM.dd}" }
}
}
}
But for custom index it creates invalid name custom-my-service-%{+YYYY.MM} and does not evaluate %{+YYYY.MM} expression.
Is it possible to evaluate field and get custom-my-service-2016.11?
If you can reformat your created field to this:
{ "customIndex" => "my-service-%Y.%m" }
Then this Ruby filter will do the trick:
ruby {
init => "require 'date'"
code => "event['indexName'] = 'custom-' + Date.today.strftime(event['customIndex'])"
}
Here is a documentation on placeholders you can use.

Is there a more beautiful way to convert a character literal to its corresponding escape character?

I'm writing a parser:
match ch {
// ...
'b' => {
token.push('\b');
continue;
},
'f' => {
token.push('\f');
continue;
},
'n' => {
token.push('\n');
continue;
},
'r' => {
token.push('\r');
continue;
},
't' => {
token.push('\t');
continue;
},
// ...
},
There's a lot of repeating code, so I'm thinking about a more elegant way to do it. I thought something like this would be possible:
macro_rules! escaped_match {
($char:expr) => (
'$char' => {
token.push('\$char')
continue;
}
)
}
But my hope is gone:
error: character literal may only contain one codepoint: '$
--> src/main.rs:3:9
|
3 | '$char' => {
| ^^
Is there a more beautiful way to do it, whether using macros, compiler plugins, hacks, or black magic?
Rust macros are not C macros — you cannot create invalid tokens and hope that they are valid sometime in the future. Likewise, they aren't a fancy way of concatenating strings that later get interpreted as code.
Looking at the code, It seems like the main repetition is in the push and continue. I'd probably use normal functions and pattern matching to DRY up that specific code:
fn escape_char(c: char) -> Option<char> {
Some(match c {
// 'b' => '\b',
// 'f' => '\f',
'n' => '\n',
'r' => '\r',
't' => '\t',
_ => return None,
})
}
fn main() {
// ...
if let Some(escape) = escape_char('b') {
token.push(escape);
continue;
}
// ...
}
Now the mapping is constrained to a single x => '\y' line.
Note that \b and \f aren't recognized escape codes in Rust; not sure what you are going to do for those.

Resources