I've been looking at rust macros recently and have found conflicting examples of macros using brackets (as laid out below). I'd like to know what the difference between each of these are and which one should be used when building macros. I'd also like to know whether any docs exist for any of this, as I can't find anything on the interwebs.
macro_rules! mac_a {
($x:ident,$y:expr) => { // <-- outer curlies
{ // <-- inner curlies
let $x = $y;
println!("{} {}", $x, $y);
}
};
}
macro_rules! mac_b {
($x:ident,$y:expr) => { // <-- outer curlies
// <-- no inner brackets / curlies
let $x = $y;
println!("{} {}", $x, $y);
};
}
// Does not compile
// macro_rules! mac_c {
// ($x:ident,$y:expr) => ( // <-- outer brackets
// ( // <-- inner brackets
// let $x = $y;
// println!("{} {}", $x, $y);
// )
// );
// }
macro_rules! mac_c2 {
($x:expr,$y:expr) => ( // <-- outer brackets
( // <-- inner brackets
println!("{} {}", $x, $y)
)
);
}
macro_rules! mac_d {
($x:ident,$y:expr) => ( // <-- outer brackets
// <-- no inner brackets / curlies
let $x = $y;
println!("{} {}", $x, $y);
);
}
fn main() {
mac_a!(a, 1);
mac_b!(b, 2);
// mac_c!(c, 3); // Does not compile
mac_c2!(3, 3);
mac_d!(d, 4);
}
All of the above except mac_c compile, and there are differences between each hence the need for mac_c2 with ident and let removed. I don't know why they can't be included ¯\_(ツ)_/¯
AFAIK the outer curlies/brackets are equivalent and simply serve to delimit each individual macro expansion. OTOH the inner curlies/brackets are part of the generated code and their contents must therefore be legal for where they are used. If we expand the macro invocations in your main functions, we get:
fn main() {
// mac_a!(a, 1);
{ // <-- inner curlies
let a = 1;
println!("{} {}", a, 1);
}
// mac_b!(b, 2);
// <-- no inner brackets / curlies
let b = 2;
println!("{} {}", b, 2);
// mac_c!(c, 3); // Does not compile
( // <-- inner brackets
let c = 3; // Invalid code
println!("{} {}", c, 3);
)
// mac_c2!(3, 3);
( // <-- inner brackets
println!("{} {}", 3, 3)
)
// mac_d!(d, 4);
// <-- no inner brackets / curlies
let d = 4;
println!("{} {}", d, 4);
}
Note BTW that there is therefore a difference for what variables are still around after the macro invocations:
fn main() {
mac_a!(a, 1);
mac_b!(b, 2);
// mac_c!(c, 3); // Does not compile
mac_c2!(3, 3);
mac_d!(d, 4);
// println!("{}", a); // Does not compile because the `let a = ...` was done inside curlies
println!("{} {}", b, d); // Work because `let b = ...` and `let d = ...` were inserted in the current scope
}
Playground
Related
i'm new to rust.
I'm trying to write a file_sensor that will start a counter after a file is created. The plan is that after an amount of time, if a second file is not received the sensor will exit with a zero exit code.
I could write the code to continue that work but i feel the code below illustrates the problem (i have also missed for example the post function referred to)
I have been struggling with this problem for several hours, i've tried Arc and mutex's and even global variables.
The Timer implementation is Ticktock-rs
I need to be able to either get heartbeat in the match body for EventKind::Create(CreateKind::Folder) or file_count in the loop
The code i've attached here runs but file_count is always zero in the loop.
use std::env;
use std::path::Path;
use std::{thread, time};
use std::process::ExitCode;
use ticktock::Timer;
use notify::{
Watcher,
RecommendedWatcher,
RecursiveMode,
Result,
event::{EventKind, CreateKind, ModifyKind, Event}
};
fn main() -> Result<()> {
let now = time::Instant::now();
let mut heartbeat = Timer::apply(
|_, count| {
*count += 1;
*count
},
0,
)
.every(time::Duration::from_millis(500))
.start(now);
let mut file_count = 0;
let args = Args::parse();
let REQUEST_SENSOR_PATH = env::var("REQUEST_SENSOR_PATH").expect("$REQUEST_SENSOR_PATH} is not set");
let mut watcher = notify::recommended_watcher(move|res: Result<Event>| {
match res {
Ok(event) => {
match event.kind {
EventKind::Create(CreateKind::File) => {
file_count += 1;
// do something with file
}
_ => { /* something else changed */ }
}
println!("{:?}", event);
},
Err(e) => {
println!("watch error: {:?}", e);
ExitCode::from(101);
},
}
})?;
watcher.watch(Path::new(&REQUEST_SENSOR_PATH), RecursiveMode::Recursive)?;
loop {
let now = time::Instant::now();
if let Some(n) = heartbeat.update(now){
println!("Heartbeat: {}, fileCount: {}", n, file_count);
if n > 10 {
heartbeat.set_value(0);
// This function will reset timer when a file arrives
}
}
}
Ok(())
}
Your compiler warnings show you the problem:
warning: unused variable: `file_count`
--> src/main.rs:31:25
|
31 | file_count += 1;
| ^^^^^^^^^^
|
= note: `#[warn(unused_variables)]` on by default
= help: did you mean to capture by reference instead?
The problem here is that you use file_count inside of a move || closure. file_count is an i32, which is Copy. Using it in a move || closure actually creates a copy of it, which does no longer update the original variable if you assign to it.
Either way, it's impossible to modify a variable in main() from an event handler. Event handlers require 'static lifetime if they reference things, because Rust cannot guarantee that the event handler lives shorter than main.
One solution for this problem is to use reference counters and interior mutability. In this case, I will use Arc for reference counters and AtomicI32 for interior mutability. Note that notify::recommended_watcher requires thread safety, otherwise instead of an Arc<AtomicI32> we could have used an Rc<Cell<i32>>, which is the same thing but only for single-threaded environments, with a little less overhead.
use notify::{
event::{CreateKind, Event, EventKind},
RecursiveMode, Result, Watcher,
};
use std::time;
use std::{env, sync::atomic::Ordering};
use std::{path::Path, sync::Arc};
use std::{process::ExitCode, sync::atomic::AtomicI32};
use ticktock::Timer;
fn main() -> Result<()> {
let now = time::Instant::now();
let mut heartbeat = Timer::apply(
|_, count| {
*count += 1;
*count
},
0,
)
.every(time::Duration::from_millis(500))
.start(now);
let file_count = Arc::new(AtomicI32::new(0));
let REQUEST_SENSOR_PATH =
env::var("REQUEST_SENSOR_PATH").expect("$REQUEST_SENSOR_PATH} is not set");
let mut watcher = notify::recommended_watcher({
let file_count = Arc::clone(&file_count);
move |res: Result<Event>| {
match res {
Ok(event) => {
match event.kind {
EventKind::Create(CreateKind::File) => {
file_count.fetch_add(1, Ordering::AcqRel);
// do something with file
}
_ => { /* something else changed */ }
}
println!("{:?}", event);
}
Err(e) => {
println!("watch error: {:?}", e);
ExitCode::from(101);
}
}
}
})?;
watcher.watch(Path::new(&REQUEST_SENSOR_PATH), RecursiveMode::Recursive)?;
loop {
let now = time::Instant::now();
if let Some(n) = heartbeat.update(now) {
println!(
"Heartbeat: {}, fileCount: {}",
n,
file_count.load(Ordering::Acquire)
);
if n > 10 {
heartbeat.set_value(0);
// This function will reset timer when a file arrives
}
}
}
}
Also, note that the ExitCode::from(101); gives you a warning. It does not actually exit the program, it only creates an exit code variable and then discards it again. You probably intended to write std::process::exit(101);. Although I would discourage it, because it does not properly clean up (does not call any Drop implementations). I'd use panic here, instead. This is the exact usecase panic is meant for.
I am new to Rust and recently started learning. I wrote a simple program which does the following.
Read a text file
Split the words and store them in a HashMap with their occurrence count
Iterate over the map and print the word and occurrence count
use std::io;
use std::env;
use std::collections::HashMap;
use std::fs;
fn main() {
let path = env::args().nth(1).unwrap();
let contents = read_file(path);
let map = count_words(contents);
print(map);
}
fn read_file(path: String) -> (String){
let contents = fs::read_to_string(path).unwrap();
return contents;
}
fn count_words(contents: String) -> (HashMap<String, i32>){
let split = contents.split(&[' ', '\n'][..]);
let mut map = HashMap::new();
for w in split{
if map.get(w) == None{
map.insert(w.to_owned(), 1);
}
let count = map.get(w);
map.insert(w.to_owned(), count + 1); // error here
}
return map;
}
fn print(map: HashMap<String, i32>){
println!("Occurences..");
for (key, value) in map.iter() {
println!("{key}: {value}");
}
}
I am able to read the file and add the words into a HashMap and print. However, while trying to add or increment, I get below error.
error[E0369]: cannot add {integer} to Option<&{integer}> -->
src\main.rs:27:40 | 27 | map.insert(w.to_owned(), count +
1); | ----- ^ - {integer} |
| | Option<&{integer}>
I know this approach should work in other languages like Java, C# etc. but unsure about how it should work in Rust.
In this block:
if map.get(w) == None{
map.insert(w.to_owned(), 1);
}
let count = map.get(w);
map.insert(w.to_owned(), count + 1); // error here
map.get(w) gives you an Option<w> (doc);
You seem to know this to some extent, as you check if it's a None earlier - the other possibility is that it is what you want - a Some(w) (not just w);
You cannot add an int to a Some, as the compiler tells you - you have to take "the w" (the count integer) out of the Some.
You can unwrap (doc), though it is not advisable.
You can use pattern matching:
match map.get(&w) {
Some(count) => { map.insert(w, count + 1); }
None => { map.insert(w, 1); }
}
Or, written differently:
if let Some(count) = map.get(&w) {
map.insert(w, count + 1);
} else {
map.insert(w, 1);
};
Or, better yet, you can use the Entry API, which turns it into a simple one liner:
*map.entry(w.to_owned()).or_default() += 1;
I am trying to write an iterator which conditionally uses elements in a separate iterator. In my example, the separate iterator should increment the sum variable. Once another condition is met *n == 4, the iterator should stop checking the condition and assume rest of elements are increments for the sum variable. I have the following working example:
fn conditional(n: &i64) -> bool {
// a lot of code here which is omitted for brevity
n % 2 == 0
}
fn main() {
let buf = vec![1,2,3,4,5,6];
let mut sum = 0;
let mut iter = buf.iter();
while let Some(n) = iter.next() {
if conditional(n) {
sum += n;
}
if *n == 4 {
// end of file - assume rest of elements are `conditional`
break;
}
};
// rest of elements [5,6]
for n in iter {
sum += n;
}
println!("sum (2+4+5+6): {:?}", sum);
}
output:
sum (2+4+5+6): 17
playground link
I would rather write the same thing with a single iterator using something like flat_map:
fn conditional(n: &i64) -> bool {
// a lot of code here which is omitted for brevity
n % 2 == 0
}
fn main() {
let buf = vec![1,2,3,4,5,6];
let mut sum = 0;
let mut terminate = false;
buf.iter().flat_map(|n| {
if *n == 4 {
// hard terminate here - return Some(n) for rest of iterator [5,6]
terminate = true;
return Some(n);
}
if terminate {
return Some(n);
}
if conditional(n) {
return Some(n);
}
None // odd
})
.for_each(|n| {
sum += n;
});
println!("sum (2+4+5+6): {:?}", sum);
}
output:
sum (2+4+5+6): 17
playground link
Is there a way to write this in a more concise manner? I want to short-circuit the iterator once the *n == 4 condition is reached.
There are many ways to solve this.
Here are a couple:
fn conditional(n: &i64) -> bool {
// a lot of code here which is omitted for brevity
n % 2 == 0
}
fn main() {
let buf = vec![1, 2, 3, 4, 5, 6];
let sum = buf
.iter()
.fold((0, false), |(mut sum, mut terminate), value| {
if *value == 4 {
terminate = true;
}
if terminate || conditional(value) {
sum += *value;
}
(sum, terminate)
})
.0;
println!("sum (2+4+5+6): {:?}", sum);
}
sum (2+4+5+6): 17
Or using filter and a stateful closure:
fn conditional(n: &i64) -> bool {
// a lot of code here which is omitted for brevity
n % 2 == 0
}
fn main() {
let buf = vec![1, 2, 3, 4, 5, 6];
let sum: i64 = buf
.iter()
.filter({
let mut terminate = false;
move |&value| {
terminate || {
if *value == 4 {
terminate = true;
}
conditional(value)
}
}
})
.sum();
println!("sum (2+4+5+6): {:?}", sum);
}
sum (2+4+5+6): 17
You can use filter():
buf.iter().filter(|n| {
if **n == 4 {
terminate = true;
}
terminate || conditional(n)
})
And sum() instead of for_each():
let sum = buf
.iter()
.filter(|n| {
if **n == 4 {
terminate = true;
}
terminate || conditional(n)
})
.sum::<i64>();
I'd like to create a custom macro similar to the standard dbg! macro, but with the option to use colors via the colored crate. dbg! usually prints something with the format of
[path_to_file:line_number] "symbol name" = "symbol value"
//[src/gallery/image_slot.rs:231] "my_integer_value_of_12" = "12"
How do I access the path/line number [path_to_file:line_number] so I can print it?
How do I access the symbol name of a variable? (i.e. print my_var given my_var = 12)
Use the file!, line!, and column! macros.
Use the stringify! macro.
If you go to the docs of the dbg! macro, you can click [src], which shows the implementation of dbg!, which is as follows:
macro_rules! dbg {
() => {
$crate::eprintln!("[{}:{}]", $crate::file!(), $crate::line!());
};
($val:expr $(,)?) => {
// Use of `match` here is intentional because it affects the lifetimes
// of temporaries - https://stackoverflow.com/a/48732525/1063961
match $val {
tmp => {
$crate::eprintln!("[{}:{}] {} = {:#?}",
$crate::file!(), $crate::line!(), $crate::stringify!($val), &tmp);
tmp
}
}
};
($($val:expr),+ $(,)?) => {
($($crate::dbg!($val)),+,)
};
}
Using that, we can easily create a similar colored_dbg! macro, with the colored crate as you suggested.
(I just picked random colors, for a simple example)
// colored = "2.0"
use colored::Colorize;
macro_rules! colored_dbg {
() => {
eprintln!("{}", format!("[{}:{}]", file!(), line!()).green());
};
($val:expr $(,)?) => {
match $val {
tmp => {
eprintln!("{} {} = {}",
format!("[{}:{}]", file!(), line!()).green(),
stringify!($val).red(),
format!("{:#?}", &tmp).blue(),
);
tmp
}
}
};
($($val:expr),+ $(,)?) => {
($(colored_dbg!($val)),+,)
};
}
You'd use it just like how you'd be able to use dbg!:
fn main() {
let my_var = 12;
colored_dbg!(&my_var);
let v = vec!["foo", "bar", "baz"];
let v = colored_dbg!(v);
}
Which outputs the following:
I am trying get something like this (doesn't work):
match input {
"next" => current_question_number += 1,
"prev" => current_question_number -= 1,
"goto {x}" => current_question_number = x,
// ...
_ => status = "Unknown Command".to_owned()
}
I tried two different versions of Regex:
go_match = regex::Regex::new(r"goto (\d+)?").unwrap();
// ...
match input {
...
x if go_match.is_match(x) => current_question_number = go_match.captures(x).unwrap().get(1).unwrap().as_str().parse().unwrap(),
_ => status = "Unknown Command".to_owned()
}
and
let cmd_match = regex::Regex::new(r"([a-zA-Z]+) (\d+)?").unwrap();
// ...
if let Some(captures) = cmd_match.captures(input.as_ref()) {
let cmd = captures.get(1).unwrap().as_str().to_lowercase();
if let Some(param) = captures.get(2) {
let param = param.as_str().parse().unwrap();
match cmd.as_ref() {
"goto" => current_question_number = param,
}
} else {
match cmd.as_ref() {
"next" => current_question_number += 1,
"prev" => current_question_number -= 1,
}
}
} else {
status = "Unknown Command".to_owned();
}
Both seem like a ridiculously long and and complicated way to do something pretty common, am I missing something?
You can create a master Regex that captures all the interesting components then build a Vec of all the captured pieces. This Vec can then be matched against:
extern crate regex;
use regex::Regex;
fn main() {
let input = "goto 4";
let mut current_question_number = 0;
// Create a regex that matches on the union of all commands
// Each command and argument is captured
// Using the "extended mode" flag to write a nicer Regex
let input_re = Regex::new(
r#"(?x)
(next) |
(prev) |
(goto)\s+(\d+)
"#
).unwrap();
// Execute the Regex
let captures = input_re.captures(input).map(|captures| {
captures
.iter() // All the captured groups
.skip(1) // Skipping the complete match
.flat_map(|c| c) // Ignoring all empty optional matches
.map(|c| c.as_str()) // Grab the original strings
.collect::<Vec<_>>() // Create a vector
});
// Match against the captured values as a slice
match captures.as_ref().map(|c| c.as_slice()) {
Some(["next"]) => current_question_number += 1,
Some(["prev"]) => current_question_number -= 1,
Some(["goto", x]) => {
let x = x.parse().expect("can't parse number");
current_question_number = x;
}
_ => panic!("Unknown Command: {}", input),
}
println!("Now at question {}", current_question_number);
}
You have a mini language for picking questions:
pick the next question
pick the prev question
goto a specific question
If your requirements end here a Regex based solution fits perfectly.
If your DSL may evolve a parser based solution is worth considering.
The parser combinator nom is a powerful tool to build a grammar starting from basic elements.
Your language has these characteristics:
it has three alternatives statements (alt!): next, prev, goto \d+
the most complex statement "goto {number}" is composed of the keyword (tag!) goto in front of (preceded!) a number (digit!).
any numbers of whitespaces (ws!) has to be ignored
These requirements translate in this implementation:
#[macro_use]
extern crate nom;
use nom::{IResult, digit};
use nom::types::CompleteStr;
// we have for now two types of outcome: absolute or relative cursor move
pub enum QMove {
Abs(i32),
Rel(i32)
}
pub fn question_picker(input: CompleteStr) -> IResult<CompleteStr, QMove> {
ws!(input,
alt!(
map!(
tag!("next"),
|_| QMove::Rel(1)
) |
map!(
tag!("prev"),
|_| QMove::Rel(-1)
) |
preceded!(
tag!("goto"),
map!(
digit,
|s| QMove::Abs(std::str::FromStr::from_str(s.0).unwrap())
)
)
)
)
}
fn main() {
let mut current_question_number = 60;
let first_line = "goto 5";
let outcome = question_picker(CompleteStr(first_line));
match outcome {
Ok((_, QMove::Abs(n))) => current_question_number = n,
Ok((_, QMove::Rel(n))) => current_question_number += n,
Err(err) => {panic!("error: {:?}", err)}
}
println!("Now at question {}", current_question_number);
}
You can use str::split for this (playground)
fn run(input: &str) {
let mut toks = input.split(' ').fuse();
let first = toks.next();
let second = toks.next();
match first {
Some("next") => println!("next found"),
Some("prev") => println!("prev found"),
Some("goto") => match second {
Some(num) => println!("found goto with number {}", num),
_ => println!("goto with no parameter"),
},
_ => println!("invalid input {:?}", input),
}
}
fn main() {
run("next");
run("prev");
run("goto 10");
run("this is not valid");
run("goto"); // also not valid but for a different reason
}
will output
next found
prev found
found goto with number 10
invalid input "this is not valid"
goto with no parameter