How do I propagate an error outside of a closure? - rust

I've got this little fragment in a function which returns Result:
list.sort_by(|a, b| a.re.partial_cmp(&b.re).unwrap()
.then(a.im.partial_cmp(&b.im).unwrap()));
but I don't like the unwrap. I'd rather replace the .unwrap() with ? but that gives me the error:
cannot use the `?` operator in a closure that returns `std::cmp::Ordering`
How can I propagate the error outside the closure?

You can't indicate an error via the return value of sort_by, and you can't abort the sort while it's under way. But you can smuggle the error out by accessing a mutable variable outside of the closure and returning a dummy value:
let mut failed = false;
list.sort_by(
|a, b| match (a.re.partial_cmp(&b.re), a.im.partial_cmp(&b.im)) {
(Some(reo), Some(imo)) => reo.then(imo),
_ => {
failed = true;
Ordering::Equal
}
},
);
match failed {
false => Ok(list),
true => Err("I think not"),
}
Playground
Though I suspect that you'd be better served by
first looking for NaNs in the list and not sorting at all if there are any (You can keep the unwraps then, as they'll really never occur), or
use something like ordered_float (or f64::total_cmp once it's stable) to make sure you can always sort (I haven't tried it yet, but it looks nice.)

Related

Reduce nested match statements with unwrap_or_else in Rust

I have a rust program that has multiple nested match statements as shown below.
match client.get(url).send() {
Ok(mut res) => {
match res.read_to_string(&mut s) {
Ok(m) => {
match get_auth(m) {
Ok(k) => k,
Err(_) => return Err(“a”);
}
},
Err(_) => {
return Err(“b”);
}
}
},
Err(_) => {
return Err(“c”);
},
};
All the variables k and m are of type String.I am looking for a way to make the code more readable by removing excessive nested match statements keeping the error handling intact since both the output and the error types are important for the problem.Is it possible to achieve this by unwrap_or_else?
The .map_err() utility converts a Result to have a new error type, leaving the success type alone. It accepts a closure that consumes the existing error value and returns the new one.
The ? operator will early-return the error in the Err case, and unwrap in the Ok case.
Combining these two allows you to express this same flow succinctly:
get_auth(
client.get(url).send().map_err(|_| "c")?
.read_to_string(&mut s).map_err(|_| "b")?
).map_err(|_| "a")?
(I suspect that you actually want to pass s to get_auth() but that's not what the code in your question does, so I'm choosing to represent the code you posted instead of imaginary code that I'm guessing about.)

How to handle errors for Not found key in Rust

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

Why can't this be done with if?

I'm trying to handle errors received from an async call:
let res: Result<TcpStream, Box<dyn std::error::Error>> = session.runtime().borrow_mut().block_on(async {
let fut = TcpStream::connect(session.configuration().socket()).await?;
Ok(fut)
});
I tried to do it the old school way with an if but the compiler didn't like it:
if res.is_err() {
return Err(res);
}
After some googling I came across this:
let mut stream = match res {
Ok(res) => res,
Err(res) => return Err(res),
};
which feels very much the same but with Rusts' equivalent of a switch statement. Why can't I use the if?
if res.is_err() { return res } should work. Result is an enum with two variants: Ok which by convention holds a "successful" result, and Err which holds error information. As John pointed out, wrapping the existing Result (which happens to hold an Err) in another Err result doesn't make sense - or, more precisely, doesn't match the return type of the function.
When you use match, you unpack the result into its constituent values, and then in the error case re-pack it into a new result. Note that instead of the match statement use can use the ? operator, which would compress the declaration to just:
let mut stream = res?;

Pass a static string to a macro_rule in rust

I wrote the following code :
macro_rules! my_macro{
("A") => {
println!("Macro called !")
}
}
fn main(){
static test: &'static str = "A";
my_macro!(test);
}
but I have the following error :
error: no rules expected the token `test`
--> test.rt:9:19
|
1 | macro_rules! my_macro{
| --------------------- when calling this macro
...
9 | my_macro!(test);
| ^^^^ no rules expected this token in macro call
error: aborting due to previous error
However, it works fine if I directly call my_macro("A"). Is it possible to fix this ?
Is it possible to fix this ?
No. Macros are expanded at compile time before item names are resolved, therefore your macro has no idea what the value of test is (and would have no idea even if it were a const rather than a static).
so the first problem here is that you macro expects a pattern of "A" not a variable that contains "A"
when you create macros you define certain patterns and follow those patterns in your case your macro must always have "A" in it but it is not a string a it is a pattern of double quote followed by capital a followed by another double quote
If you want to pass a value you should use variable syntax and define what it should expect such as ($a:expr)=>{...}
here you can see all magic tokens possible just scroll down a bit on that docs there are a lot of great examples
PS. here is a macro I use for responding from my endpoints
macro_rules! resp {
(ok) => {
|_| actix_web::HttpResponse::Ok().body(r#"{"success":true}"#)
};
(ok,$data:expr) => {
|_| actix_web::HttpResponse::Ok().json(serde_json::json!({"success":true,"data":$data}))
};
(ok,) => {
|d| actix_web::HttpResponse::Ok().json(serde_json::json!({"success":true,"data":d}))
};
}

Error 'cannot move out of dereference' when trying to match strings from vector

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() {
/* ... */
}

Resources