I'm trying to capture errors on Rust, but I don't want the program to exit if I found it. I'm very new to Rust. Basically, I want to find a key from the Windows registry and if it doesn't exist, then create it.
Im using a crate called winreg for that.
This would be a section of my program:
fn main() -> io::Result<()> {
...
...
...
key.set_value("TestSZ", &"written by Rust")?;
// here I'm getting a value that exists
let sz_val: String = key.get_value("TestSZ")?;
// but this key doesn't exist
let other: String = key.get_value("NOT_EXISTING_KEY")?;
println!("TestSZ = {}", sz_val);
println!("TestSZ = {}", other);
Ok(())
}
If I compile that I receive this in the console:
And now lets write something...
An existing key has been opened
TestSZ = written by Rust
Error: Os { code: 2, kind: NotFound, message: "Couldn't find the pecified file." }
error: process didn't exit successfully: `target\debug\playground.exe` (exit code: 1)
In a pseudocode way, I would like something like:
if other == null {
println!("Nothing found!");
create_key();
}
If I analize get_value it looks like this:
pub fn get_value<T: FromRegValue, N: AsRef<OsStr>>(&self, name: N) -> io::Result<T>
I don't know what that means. I've been reading about errors and everything I do fails.
If I do let other: String = key.get_value("NOT_EXISTING_KEY").expect("Failed to read product name"); then the program exits, showing the error.
But I don't want the program to fail, I want to capture the error and do a different flow if I don't find the key (for example, create it).
Does anyone know how can I deal with this?
In Rust, a function that can fail usually returns a Result<OkType, ErrorType> data type. This type is a structured enum, which means, that it can tell you not only if error has occured, but also what kind of error, so you could act accordingly.
You can process enums with match statements. Or alternatively, Result type has shortcuts like Result::unwrap or Result::expect that basically say: "If there is an error, just tell me what kind and crash the program."
I'm not very familiar with Windows Registry, so I'm not sure how bulletproof the following code snippet is, but it should give you an idea on how you can process errors with a match statement.
fn main() {
// ...
let anykey_value = match key.get_value("AnyKey") {
// If the key is present, initialize `anykey_value` variable
// with the returned value
Ok(value) => value,
// If the key is not found, do the following steps:
Err(error) => {
println!("Nothing found!");
// Try to set an empty string as the value for "AnyKey".
// If fails: panic with the following message.
key.set_value("AnyKey", &"").expect("Failed to create key \"AnyKey\"");
// Initialize `anykey_value` variable with an empty string.
""
}
}
// Will print the value stored in "AnyKey"
// or an empty string, if the key was just created.
println!("AnyKey = {}", anykey_value);
// ...
}
Also you can checkout Error handling chapter from The Rust Programming Language book. It might be helpful.
I got it this way:
match key.get_value("NOT_EXISTING_KEY") {
Ok(value) => {
println!("found: {}", value);
value
},
Err(err) => {
println!("not found: {}", err);
String::from("")},
};
It was expecting a String
Related
I wanted to be able to retrieve the content from an attribute like this:
#[foreign_key(table = "some_table", column = "some_column")]
This is how I am trying:
impl TryFrom<&&Attribute> for EntityFieldAnnotation {
type Error = syn::Error;
fn try_from(attribute: &&Attribute) -> Result<Self, Self::Error> {
if attribute.path.is_ident("foreign_key") {
match attribute.parse_args()? {
syn::Meta::NameValue(nv) =>
println!("NAME VALUE: {:?}, {:?}, {:?}",
nv.path.get_ident(),
nv.eq_token.to_token_stream(),
nv.lit.to_token_stream(),
),
_ => println!("Not interesting")
}
} else {
println!("No foreign key")
}
// ... More Rust code
}
Everything works fine if I just put in there only one NameValue. When I add the comma,
everything brokes.
The only error:
error: unexpected token
How can I fix my logic to enable the possibility of have more than just one NameValue?
Thanks
UPDATE: While writing this answer, I had forgotten that Meta has List variant as well which gives you NestedMeta. I would generally prefer doing that instead of what I did in the answer below for more flexibility.
Although, for your particular case, using Punctuated still seems simpler and cleaner to me.
MetaNameValue represents only a single name-value pair. In your case it is delimited by ,, so, you need to parse all of those delimited values as MetaNameValue instead.
Instead of calling parse_args, you can use parse_args_with along with Punctuated::parse_terminated:
use syn::{punctuated::Punctuated, MetaNameValue, Token};
let name_values: Punctuated<MetaNameValue, Token![,]> = attribute.parse_args_with(Punctuated::parse_terminated).unwrap(); // handle error instead of unwrap
Above name_values has type Punctuated which is an iterator. You can iterate over it to get various MetaNameValue in your attribute.
Updates based on comments:
Getting value out as String from MetaNameValue:
let name_values: Result<Punctuated<MetaNameValue, Token![,]>, _> = attr.parse_args_with(Punctuated::parse_terminated);
match name_values {
Ok(name_value) => {
for nv in name_value {
println!("Meta NV: {:?}", nv.path.get_ident());
let value = match nv.lit {
syn::Lit::Str(v) => v.value(),
_ => panic!("expeced a string value"), // handle this err and don't panic
};
println!( "Meta VALUE: {:?}", value )
}
},
Err(_) => todo!(),
};
I am trying to achieve something like this:
Construct an error message with some captured variables;
Log this error message;
Save and return this error message to user eventually;
Apparently this can be done in a few lines of code, but it is a bit tedious since i need this pattern heavily everywhere in my project, i mean like below:
let var_a = 233;
let err = format!("some custom error message, var_a: {}", var_a);
// Here i do the logging with the `tokio-rs/tracing` macros.
error!(err = %err, "some custom error message");
// The s_errs will be eventually showed to end users.
s_errs.append(err);
I am trying to achieve this with a macro which wraps the tokio-rs/tracing macros but return the constructed err message:
#[macro_export]
macro_rules! log_usr_err {
($($arg:tt)+) => (
error!($($arg)+)
format!($($arg)+)
);
}
so i can just use this macro in my project to simplify the coding a bit:
let err = log_usr_err!("some custom error message, {}", var_a = var_a);
// The s_errs will be eventually showed to end users.
s_errs.append(err);
but stuck at the compile error:
error: macro expansion ignores token `format` and any following
How should i return the formatted string from my log_urs_err! macro?
Coz in the future in want to include the accurate file! info and the line! info also in my logs, thus i think via macro is the right direction to go, or there is some better approach?
Let's just try and look at what the expanded code would look like in your case. Reiterating the code in question:
#[macro_export]
macro_rules! log_usr_err {
($($arg:tt)+) => (
error!($($arg)+)
format!($($arg)+)
);
}
let err = log_usr_err!("some custom error message, {}", var_a = var_a);
If we try to literally substitute the result of macro expansion, we'd get the following:
let err = error!("some custom error message, {}", var_a = var_a)
format!("some custom error message, {}", var_a = var_a);
This is obviously not valid Rust.
The problem is that your macro is currently written to expand to the list of statements, i.e. to some self-contained part of the code (of course, after adding the necessary semicolon between error and format). It can be, for example, a function body (playground):
macro_rules! log_usr_err {
($($arg:tt)+) => (
error!($($arg)+);
format!($($arg)+)
);
}
fn err() {
log_usr_err!("test");
}
But you'd like to use this macro in expression position, i.e. to assign the value returned from it to the variable. For this, macro must expand to the expression, not to statements.
So, what to do? The change is, in fact, quite simple: a set of statements can be converted to expression by wrapping them in a block, since blocks are expressions:
macro_rules! log_usr_err {
($($arg:tt)+) => ({
error!($($arg)+);
format!($($arg)+)
});
}
Note the extra braces around the macro content.
With this change, your code compiles.
I was working in coding dojo trying to learn Rust. In the attached link is all our code and test. However, we got stumped as to why we required calling clone() in one function but not the other.
Why do I need to call game.clone() on line 23 of lib.rs in this link https://cyber-dojo.org/kata/edit/WvEB5z
pub fn say_game_score(game: Game) -> String {
if game.player1.score == game.player2.score {
return say_equal_score(game.player1.score);
}
if can_be_won(game) { // This line required game.clone() WHY???
return say_winning_situation(game); // This line does NOT require game.clone()
}
return format!(
"{} {}",
say_score_name(game.player1.score),
say_score_name(game.player2.score)
);
}
fn say_winning_situation(game: Game) -> String {
if game.player1.score > game.player2.score {
return say_leading_situation(game.player1.name, game.player1.score - game.player2.score);
} else {
return say_leading_situation(game.player2.name, game.player2.score - game.player1.score);
}
}
fn can_be_won(game: Game) -> bool {
return game.player1.score > FORTY || game.player2.score > FORTY;
}
can_be_won(game) causes the variable game to be moved into the function. When you then call say_winning_situation(game) the variable has already moved and cant be used anymore. The Rust compile can actually check these things.
The compiler suggests that you clone the game in the first invocation, so it will be copied instead of moved.
You probably want to use references instead of values in your functions. Only take ownership when you need it. For reading access a reference (which is const by default) is your first choice.
You should read about borrow checking in Rust.
I'm learning rust and trying to make a find like utility (yes another one), im using clap and trying to support command line and config file for the program's parameters(this has nothing to do with the clap yml file).
Im trying to parse the commands and if no commands were passed to the app, i will try to load them from a config file.
Now I don't know how to do this in an idiomatic way.
fn main() {
let matches = App::new("findx")
.version(crate_version!())
.author(crate_authors!())
.about("find + directory operations utility")
.arg(
Arg::with_name("paths")
...
)
.arg(
Arg::with_name("patterns")
...
)
.arg(
Arg::with_name("operation")
...
)
.get_matches();
let paths;
let patterns;
let operation;
if matches.is_present("patterns") && matches.is_present("operation") {
patterns = matches.values_of("patterns").unwrap().collect();
paths = matches.values_of("paths").unwrap_or(clap::Values<&str>{"./"}).collect(); // this doesn't work
operation = match matches.value_of("operation").unwrap() { // I dont like this
"Append" => Operation::Append,
"Prepend" => Operation::Prepend,
"Rename" => Operation::Rename,
_ => {
print!("Operation unsupported");
process::exit(1);
}
};
}else if Path::new("findx.yml").is_file(){
//TODO: try load from config file
}else{
eprintln!("Command line parameters or findx.yml file must be provided");
process::exit(1);
}
if let Err(e) = findx::run(Config {
paths: paths,
patterns: patterns,
operation: operation,
}) {
eprintln!("Application error: {}", e);
process::exit(1);
}
}
There is an idiomatic way to extract Option and Result types values to the same scope, i mean all examples that i have read, uses match or if let Some(x) to consume the x value inside the scope of the pattern matching, but I need to assign the value to a variable.
Can someone help me with this, or point me to the right direction?
Best Regards
Personally I see nothing wrong with using the match statements and folding it or placing it in another function. But if you want to remove it there are many options.
There is the ability to use the .default_value_if() method which is impl for clap::Arg and have a different default value depending on which match arm is matched.
From the clap documentation
//sets value of arg "other" to "default" if value of "--opt" is "special"
let m = App::new("prog")
.arg(Arg::with_name("opt")
.takes_value(true)
.long("opt"))
.arg(Arg::with_name("other")
.long("other")
.default_value_if("opt", Some("special"), "default"))
.get_matches_from(vec![
"prog", "--opt", "special"
]);
assert_eq!(m.value_of("other"), Some("default"));
In addition you can add a validator to your operation OR convert your valid operation values into flags.
Here's an example converting your match arm values into individual flags (smaller example for clarity).
extern crate clap;
use clap::{Arg,App};
fn command_line_interface<'a>() -> clap::ArgMatches<'a> {
//Sets the command line interface of the program.
App::new("something")
.version("0.1")
.arg(Arg::with_name("rename")
.help("renames something")
.short("r")
.long("rename"))
.arg(Arg::with_name("prepend")
.help("prepends something")
.short("p")
.long("prepend"))
.arg(Arg::with_name("append")
.help("appends something")
.short("a")
.long("append"))
.get_matches()
}
#[derive(Debug)]
enum Operation {
Rename,
Append,
Prepend,
}
fn main() {
let matches = command_line_interface();
let operation = if matches.is_present("rename") {
Operation::Rename
} else if matches.is_present("prepend"){
Operation::Prepend
} else {
//DEFAULT
Operation::Append
};
println!("Value of operation is {:?}",operation);
}
I hope this helps!
EDIT:
You can also use Subcommands with your specific operations. It all depends on what you want to interface to be like.
let app_m = App::new("git")
.subcommand(SubCommand::with_name("clone"))
.subcommand(SubCommand::with_name("push"))
.subcommand(SubCommand::with_name("commit"))
.get_matches();
match app_m.subcommand() {
("clone", Some(sub_m)) => {}, // clone was used
("push", Some(sub_m)) => {}, // push was used
("commit", Some(sub_m)) => {}, // commit was used
_ => {}, // Either no subcommand or one not tested for...
}
I am very new to rust and trying to write a command line utility as a way to learn.
I am getting the list of args and trying to match on them
let args = os::args()
//some more code
match args[1].into_ascii_lower().as_slice() {
"?" | "help" => { //show help },
"add" => { //do other stuff },
_ => { //do default stuff }
}
this causes this error
cannot move out of dereference (dereference is implicit, due to indexing)
match args[1].into_ascii_lower().as_slice() {
^~~~~~~
I have no idea what that means, but searching yield this which I didn't completely get, but changing the args[1] to args.get(1) gives me another error
error: cannot move out of dereference of `&`-pointer
match args.get(1).into_ascii_lower().as_slice() {
^~~~~~~~~~~
what's going on?
As you can see in the documentation, the type of into_ascii_lower() is (see here) :
fn into_ascii_upper(self) -> Self;
It takes self directly, not as a reference. Meaning it actually consumes the String and return an other one.
So, when you do args[1].into_ascii_lower(), you try to directly consume one of the elements of args, which is forbidden. You probably want to make a copy of this string, and call into_ascii_lower() on this copy, like this :
match args[1].clone().into_ascii_lower().as_slice() {
/* ... */
}