I wrote this code:
//Amazing log function that can take up to three parameters!!!
macro_rules! log {
// todo variadic
() => {
let string = " ";
info!(" ");
};
($a:expr) => {
let string = format!("{}", format_args!("{}", $a));
info!("{}", string);
};
($a:expr,$b:expr) => {
let string = format!("{}", format_args!("{}{}", $a, $b));
info!("{}", string);
};
($a:expr,$b:expr,$c:expr) => {
let string = format!("{}", format_args!("{}{}{}", $a, $b, $c));
info!("{}", string);
};
}
Example usage:
let number = 163.19;
log!("Heeey fam is this number right: ", number, ". No, it's wrong");
//output: [TIME INFO project] Hey fam is this number correct: 163.19. No, it's wrong
I want it to be able to take any number of parameters is that possible in rust?
This is it.
macro_rules! log {
($($args:expr),*) => {
let mut result: String = String::from("");
$(
let tempstr: String = format!("{}", format_args!("{}", $args));
result.push_str(&tempstr[..]);
)*
info!("{}", result);
};
}
Related
I finished converting the Rust books guessing game example to an Iced GUI application and wanted to handle error handling for the input from the user.
I have a String trying to convert to an i32 and am not sure how to handle the error if the user puts a String in the text_input or just hits return. I figured out a simple solution of:
self.hidden_compare = self.user_guess_text.trim().parse::<i32>().unwrap_or(0);
Rather than having self.hidden_compare default to 0. I would rather have self.user_guess_text default to a String I have set to earlier in the application and am unsure of how to accomplish this still being fairly new.
Edit: Full function added for clarification.
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::BtnGuessNow => {
self.hidden_compare = self.user_guess_text.trim().parse::<i32>().unwrap_or(0);
if self.hidden_value == self.hidden_compare {
self.label_compare = String::from("A WINNER!");
self.number_ofguesses.push(self.hidden_compare.to_string() + ", A WINNER!");
}
else if self.hidden_value > self.hidden_compare {
self.label_compare = String::from("Too Low");
self.number_ofguesses.push(self.hidden_compare.to_string() + ", Too Low!");
}
else if self.hidden_value < self.hidden_compare {
self.label_compare = String::from("Too Big");
self.number_ofguesses.push(self.hidden_compare.to_string()+ ", Too Big!");
}
self.user_guess_text = "".to_string();
Command::none()
}
Message::UserInputValueUpdate(x) => {
self.user_guess_text = x;
Command::none()
}
}
and a relevant function that handles the Vec output:
fn guess_output_calc(&self) -> String {
let mut tempoutput = String::new();
for (i, x) in self.number_ofguesses.iter().enumerate().skip(1) {
let guessfmt = String::from(format!("Guess# {} Was: {}\n", i, x));
tempoutput.push_str(&guessfmt);
};
return
I would rather have self.user_guess_text default to a String
I'm not entirely sure what you mean with that, but I interpret this as "I want to set the self.user_guess_text variable to a specific value if it can't be converted to an integer". If this is wrong, then please update your question.
This is how I would approach this (simplified):
fn main() {
let mut user_guess_text = " a ";
match user_guess_text.trim().parse::<i32>() {
Ok(value) => {
println!("Parsed to value: {}", value);
}
Err(_) => {
println!("Unable to parse. Resetting variable.");
user_guess_text = "fallback text!";
}
}
println!("user_guess_text: {}", user_guess_text);
}
Unable to parse. Resetting variable.
user_guess_text: default text!
I'm new to Rust and am trying to wrap my head around error handling.
I'm trying to return error if parsing the date goes wrong, here is the function:
pub fn create_posts(contents: &Vec<String>) -> Result<Vec<Post>, CreatePostError> {
const TITLE_SEP: &str = "Title: ";
const DESC_SEP: &str = "Description: ";
const DATE_SEP: &str = "Date: ";
const TAGS_SEP: &str = "Tags: ";
let mut posts: Vec<Post> = Vec::new();
for entry in contents {
let lines = entry.lines().collect::<Vec<_>>();
let metadata = lines[0].contains(&TITLE_SEP)
&& lines[1].contains(&DESC_SEP)
&& lines[2].contains(&DATE_SEP)
&& lines[3].contains(&TAGS_SEP);
if metadata {
let date = &lines[2][DATE_SEP.len()..];
let parsed_date = match NaiveDate::parse_from_str(date, "%Y-%m-%d") {
Ok(parsed_date) => parsed_date,
Err(e) => eprintln!("Error: {:?}", CreatePostError::ParseError { inner_err: e }),
};
let tags: Vec<String> = lines[3][TAGS_SEP.len()..]
.split(", ")
.map(|s| s.to_string())
.collect();
let mut article_content = String::new();
for line in &lines[4..] {
article_content.push_str(line);
article_content.push_str("\n")
}
let post = Post {
title: lines[0][TITLE_SEP.len()..].to_string(),
description: lines[1][DESC_SEP.len()..].to_string(),
date: parsed_date,
tags,
content: article_content,
};
posts.push(post);
} else {
return Err(CreatePostError::MetadataError);
}
}
return Ok(posts);
}
You can see the full code here since i wrote custom errors: link
The problem I'm having is with this part:
let date = &lines[2][DATE_SEP.len()..];
let parsed_date = match NaiveDate::parse_from_str(date, "%Y-%m-%d") {
Ok(parsed_date) => parsed_date,
Err(e) => eprintln!("Error: {:?}", CreatePostError::ParseError { inner_err: e }),
};
I'm getting match arms have incompatible types. Expected struct NaiveDate, found ()
Here is my enum and impl for the error:
#[derive(Debug)]
pub enum CreatePostError {
ReadFileError { path: PathBuf },
MetadataError,
ParseError { inner_err: ParseError },
}
impl fmt::Display for CreatePostError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::ReadFileError { path } => write!(f, "Error reading file {path:?}"),
Self::MetadataError => write!(f, "Some metadata is missing"),
Self::ParseError { inner_err } => {
write!(f, "Error parsing date: {inner_err}")
}
}
}
}
impl From<chrono::format::ParseError> for CreatePostError {
fn from(e: chrono::format::ParseError) -> Self {
CreatePostError::ParseError { inner_err: e }
}
}
You probably want to have a result here, here is a way to do it:
let parsed_date = match NaiveDate::parse_from_str(date, "%Y-%m-%d") {
Ok(parsed_date) => Ok(parsed_date),
Err(e) => {eprintln!("Error: {:?}", &e);
Err(CreatePostError::ParseError { inner_err: e }}),
}?;
The ? is saying: if the thing before is an error, return it, and if it is Ok, unwrap it.
This pattern is so common that rust's result gives a lot of utilities to make this kind of things easier. Here, the map_err function would make it more straightforward: map_err
see:
let parsed_date = NaiveDate::parse_from_str(date, "%Y-%m-%d")
.map_err(|e| {
eprintln!("Error: {:?}", &e);
CreatePostError::ParseError { inner_err: e }})?;
But it is only a matter of preference and it might be a lot to digest if you are just beginning, so you can choose the way that you like the most.
I have this code here, where I try to extract some text based on a delimiter:
//not working
let mut text: Option<String> = None;
for d in DELIMITERS {
let split_res = full_text.split_once(d);
if let Some((_, t0)) = split_res {
let t = t0.to_string();
match text {
None => text = Some(t),
Some(t2) => if t.len() < t2.len() {
text = Some(t);
}
}
}
}
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=8e4e0e8d7b2271b8f8ebd126896236ea
But I get these errors from the compiler
^^ value moved here, in previous iteration of loop
note: these 2 reinitializations might get skipped
What's going on here?
Why can't I pattern match on text? Is the problem that my text variable gets consumed? I can't really understand where and how?
Changing the code to use as_ref() on text fixes the error but I don't understand why this is necessary:
//working
let mut text: Option<String> = None;
for d in DELIMITERS {
let split_res = full_text.split_once(d);
if let Some((_, t0)) = split_res {
let t = t0.to_string();
match text.as_ref() {
None => text = Some(t),
Some(t2) => if t.len() < t2.len() {
text = Some(t);
}
}
}
}
If you do not use as_ref, you are moving the object hence consuming it, so it is not available in the next iteration.
You can also match a reference:
match &text {}
Playground
Or you can take the inner value, leaving a None behind, that way you do not drop it. And since you re-assignate it afterwards keeps the same functionality:
const DELIMITERS: [&'static str; 2] = [
"example",
"not-found",
];
fn main() {
let full_text = String::from("This is an example test");
let mut text: Option<String> = None;
for d in DELIMITERS {
let split_res = full_text.split_once(d);
if let Some((_, t0)) = split_res {
let t = t0.to_string();
match text.take() {
None => text = Some(t),
Some(t2) => if t.len() < t2.len() {
text = Some(t);
}
}
}
}
if let Some(t) = text {
println!("{}", t);
}
}
Playground
I’d like to have a code that supposed to achieve this in end.
counters!(NAME1, “unit1”, NAME2, “unit2”)
// generates
const NAME1 = 0;
const NAME2 = 1;
static mut counters = [AtomicUsize::new(0), AtomicUsize::new(0)];
static units = [“unit1”, “unit2”];
So far I’ve been able to create indices names and the array of arbitrary values, but I have troubles combining this things together.
playgound link
use std::sync::atomic::AtomicUsize;
macro_rules! gen_array {
($out:expr; ) => { $out };
([$($out:tt)*]; $name:ident, $($names:tt)*) => {
gen_array!([$($out)* $name,]; $($names)*)
};
}
macro_rules! gen_vars {
($cnt:expr; ) => {};
($cnt:expr; $name:ident, $($names:tt)*) => {
const $name: usize = $cnt;
gen_vars!($cnt + 1; $($names)*)
};
}
macro_rules! counters {
($($name:ident),+) => {
gen_vars!(0; $($name),+,);
gen_array!([]; $($name),+,);
};
}
fn main() {
let arr = counters!(ONE, TWO);
dbg!(arr);
}
This seems to do what you want.
use std::sync::atomic::AtomicUsize;
macro_rules! gen_array {
($out:expr; ) => { $out };
([$($out:tt)*]; $name:literal, $($names:tt)*) => {
gen_array!([$($out)* $name,]; $($names)*)
};
}
macro_rules! gen_vars {
($cnt:expr; ) => {};
($cnt:expr; $name:ident, $($names:tt)*) => {
const $name: usize = $cnt;
gen_vars!($cnt + 1; $($names)*)
};
}
macro_rules! counters {
($($name:ident,$value:literal),+) => {
gen_vars!(0; $($name),+,);
gen_array!([]; $($value),+,);
};
}
fn main() {
counters!(ONE, "unit1", TWO, "unit2");
}
Playground
I have this code below that is repeated in many places in my application.
The only thing that differs on the per-command basis is the if let Role { whatever } = role line
#[command]
pub async fn protect(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
let target_tag: String = args.single()?;
let (user_id, guild_id) = msg.get_ids();
let (target_id, _) = LupusCtxHelper::parse_tag_to_target_id(ctx, Tag(target_tag))
.await
.ok_or(MyError)?;
let player = {
let dt = ctx.data.read().await;
dt.get_player(&guild_id, &user_id).await
};
if let Some(p) = player {
// this line is the problem
if let LupusRole::BODYGUARD { .. } = *p.current_role() {
LupusCtxHelper::send_lupus_command(ctx, msg, LupusAction::Protect(target_id)).await?
} else {
msg.channel_id
.say(&ctx.http, "check your role dude")
.await?;
}
}
Ok(())
}
How would you guys suggest I go about cleaning up this code/refactoring in an external function? It seems like I can't pattern match dynamically on the left.
ps: beware that the enum LupusRole has struct values
Example of another file:
#[command]
pub async fn frame(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
let target_tag: String = args.single()?;
let (user_id, guild_id) = msg.get_ids();
let (target_id, _) = LupusCtxHelper::parse_tag_to_target_id(ctx, Tag(target_tag))
.await
.ok_or(MyError)?;
let player = {
let dt = ctx.data.read().await;
dt.get_player(&guild_id, &user_id).await
};
if let Some(p) = player {
if let LupusRole::GUFO = *p.current_role() {
LupusCtxHelper::send_lupus_command(ctx, msg, LupusAction::Frame(target_id)).await?
} else {
msg.channel_id
.say(&ctx.http, "fra... ruolo sbagliato")
.await?;
}
}
Ok(())
}