What's the functional approach to replace this match block? - rust

What's the functional approach to replace the below match?
match second_db.player_create(player_id).await {
Ok(o) => Ok(o),
Err(err) => {
first_db.player_delete(player_id).await?;
Err(err)
}
}

As others have pointed out, the usual helper methods on Result and Option do not work with async (see links below). However, these methods are mostly about transforming the Result / Option which you are not doing. Hence, your code could be rephrased to the following (assuming you want to return the result of the match expression):
let result = second_db.player_create(player_id).await;
if result.is_err() {
first_db.player_delete(player_id).await?;
}
result
This omits the "mapping" part of both branches and is, in my opinion, easier to understand.
Related discussions:
How to use async/await inside closure of `Option::and_then` or `Option::map` without using OptionFuture?
https://users.rust-lang.org/t/how-to-run-async-code-within-option-result-function-chain/64053

Related

Replacing Unwrap()s with ? - compiler complaining on functions that do return Result or Option [duplicate]

This question already has answers here:
What is this question mark operator about?
(4 answers)
Why do try!() and ? not compile when used in a function that doesn't return Option or Result?
(4 answers)
the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
(1 answer)
What is the idiomatic way to handle/unwrap nested Result types?
(2 answers)
Closed 7 months ago.
The community reviewed whether to reopen this question 7 months ago and left it closed:
Original close reason(s) were not resolved
I'm only a few days into my Rust journey and going over my now functionally completed first project, working on exercising unwrap()s from my code.
The first two instances I've tried, I've completely failed and I cannot work out why. I have this line in my code:
let json = str::from_utf8(&buf).unwrap();
from_utf8 returns Result<&str, Utf8Error>, but trying:
let json = str::from_utf8(&buf)?;
Has a compiler error of "This function should return Result or Option to accept ?", but it does return Result. I can only assume that the pointer & is having an effect here?
I've refactored this now to:
match str::from_utf8(&buf) {
Ok(json) => {
let msg: MyMessage = serde_json::from_str(json).unwrap();
self.tx.try_send((msg, src)).expect("Could not send data");
}
Err(e) => {}
};
I still need to work out what to do in the Err but I've gotten rid of the unwrap() call. However, there's another.
serde_json::from_str(json).unwrap();
This returns Result<T>, so:
serde_json::from_str(json)?;
This is also complaining about the fact it should return Result when it already does.
At this point, it's safe to assuming I'm really confused and I don't understand half as much as a thought I did.
Countless blogs just say use ?, yet, in every instances I think it should work and the return types appear suitable, the compiler says no.
Would the From trait work here? Is it common to have to write traits like this?
from_utf8 returns Result<&str, Utf8Error>, but trying:
let json = str::from_utf8(&buf)?;
Has a compiler error of This function should return Result or Option to accept ?. But it does return Result.
This function here refers to the outer function. That is, the function you are writing, not from_utf8.
? will return an error from your function if there was one, thus your function needs the right return type to be able to return an error.
Because from_utf8 is not the source of the issue, you'd get the same error like this too:
let r = Err(...);
let json = r?;
The ? operator is roughly equivalent to a match with an early return for the error case, so in your example
fn foo() {
let json = str::from_utf8(&buf)?;
// ...
}
is roughly equivalent to
fn foo() {
let json = match str::from_utf8(&buf) {
Ok(j) => j,
Err(e) => return Err(e.into()),
}
// ...
}
This should make it clear where the error is coming from. You are trying to return a Result from a function that doesn't return anything.

How can I match a single condition in Rust; 'match', 'if matches!', or 'if let'?

I want to check a condition that depends only on its structure (but not the actual values it holds), and do something in either case. I can think of three ways do this: with match, if let, or if matches!. I'm not sure if there are any drawbacks to any of these or if they are effectively equivalent.
Am I safe to use any of these or are there known drawbacks that I might run into with one or more of these? Is there a fourth way that I haven't thought of?
match self.context.get(s) {
Some(Bound::Function(_, _)) => Err("Parse Error".to_string())
_ => {
self.context.insert(s.clone(), ...);
Ok(None)
}
}
if let Some(Bound::Function(_, _)) = self.context.get(s) {
Err("Parse Error".to_string())
}
else {
self.context.insert(s.clone(), ...);
Ok(None)
}
if matches!(self.context.get(s), Some(Bound::Function(_, _))) {
Err("Parse Error".to_string())
}
else {
self.context.insert(s.clone(), ...);
Ok(None)
}
Your three solutions are equivalent. As of today they even compile to the same machine code: https://godbolt.org/z/d4acvY5G3
Performance-wise, you would need to benchmark but I suspect that the fastest would be to use the entry API. Something along the lines of:
match self.context.entry (s) {
Entry::Occupied (_) => Err("Parse Error".to_string()),
Entry::Vacant (e) => {
e.insert (Bound::Number(evaluate(&self.context, parseadd(tokens))?));
Ok (None)
}
}
Note that depending on the rest of your code, this solution might require to always clone s whereas yours don't clone it if the entry already exists. However:
Your solutions only avoid cloning when an error occurs, which should be a rare occurrence and less performance-sensitive than the non-error case.
If performance is really an issue, it would probably be best to avoid cloning altogether if possible (but that depends on where s comes from).
As with all performance-related questions, you should measure it in your environment to see if it brings any significant improvement (and you should only try to improve it after you have shown that it is a performance bottleneck for your application).

How can I best pattern match in Result::map

I know I can pattern match like this in rust
some_result.map(|some_number| {
match some_number {
1 => HttpResponse::NoContent().finish(),
_ => HttpResponse::NotFound().finish(),
}
})
but in Scala I can do like this
some_option.map {
case 1 => ???
case _ => ???
}
Is there a way to avoid the repetition of the variable some_number in the rust code above?
EDIT:
I found out i could do it this way, but i still think the original question answered my question best.
Ok(match result {
Ok(1) => HttpResponse::NoContent(),
Ok(_) => HttpResponse::NotFound(),
Err(_) => HttpResponse::InternalServerError()
}.finish())
its all about the context and in this case i didnt include much of it ...
EDIT #2:
Changed to another answer. I really like inverting the problem. And if else is not idiomatic rust afaik.
If we're just bike-shedding style, you could avoid introducing some_number entirely by matching on the whole result:
match some_result {
Ok(1) => Ok(HttpResponse::NoContent().finish()),
Ok(_) => Ok(HttpResponse::NotFound().finish()),
Err(e) => Err(e)
};
But this just trades some_number for some Oks and Errs. I would generally prefer the original style, but beauty is in the eye of the beholder.
There is no way that I know of to avoid the repetition, however I think it might be more idiomatic to simply write
some_result.map(
|some_number|
if some_number == 1 {
HttpResponse::NoContent().finish()
} else {
HttpResponse::NotFound().finish()
}
)
since there is no need for a match in such a simple situation.
EDIT: Why is an if statement more idiomatic than a match on in this situation?
The general idea is that match is more powerful than if (every if statement could be replaced by a match statement), therefore if is more specific, and thus should be used when possible (without matches!). The only exception is the switch/case use-case, which could be expressed as an if statement but a match one should be used.
But this is more of a guideline than an argument, so let's break down the reason why if is more idiomatic.
You start with something like
match some_number {
1 => { ... }
_ => { ... }
}
In the situation of
match x {
Pattern => { ... }
_ => { ... }
}
if let is more idiomatic. Since we're in this situation, we can rewrite
if let 1 = some_number { ... } else { ... }
However, in our case, we are matching a single literal, so it is more idiomatic to simply transform the if let into
if some_number == 1 { ... } else { ... }
The only exception is when you are planning to add more branching to the match statement, like
match some_number {
1 => { ... }
2 => { ... }
_ => { ... }
}
in which case it would make sense to keep it like that.
Keep in mind that being idiomatic also means being able to convey by the way you code your intention so that your programming becomes clear.
Note: Why is this more idiomatic than than simply matching the whole result?
Most of the time, being idiomatic is a synonym of being concise. If you are being verbose, it's a good hint you're not being idiomatic. However, it'is not always true, and this is a good example of being idiomatic meaning being more verbose.
When you are matching a result, you are expressing that you want to handle both the error and the ok case. When you are mapping, you are instead expressing that you are only interested in the ok case.
Most of the time, people don't want to handle manually the error case, and just add a ?. However, when they don't, most of the time they want to handle the error case. Finally, they might want not to handle the error, but also not to get rid of it right away.
These three choices are increasingly verbose to implement due to the frequency of usage. This means that you should not aim for the one that is less verbose, but instead for the solution that matches your intention, so that when one reads your code, it's easier to grasp your intention just by your choice structure of implementation.
In your original question, you seemed not to care about the error case, and also you didn't seem to want to get rid of it with ?, which is why I think that having an if statement inside a map is more idiomatic, in the sense that it is more clear and communicates better what you want to achieve. Indeed, I didn't even think about the error case, which is, IMO, what idiomatic means (ie. the capacity of adapting the way one thinks to ease the comprehension of code by writing it in the most expressive way, for a given language).
Finally, I would point out the most idiomatic choice for handling an error, that you didn't seem to take into account, and I wonder why.
if some_result? == 1 {
HttpResponse::NoContent().finish()
} else {
HttpResponse::NotFound().finish()
}
Where you have implemented an appropriate conversion from the eventual error type to the return type.

Options, and_then() and tuples

I'm convinced there is a way to handle this 'cleanly', I am just not quite figuring it out.
use git2::Repository;
// Prints out the current branch and sha if it exists.
fn report_repo() -> () {
Repository::open(".")
.ok()
.and_then(branch_and_sha)
.and_then(|branch_sha| => { // Fails here with E0061
let (branch, sha) = branch_sha;
println!("Branch={} sha={}", branch, sha);
None
});
}
fn branch_and_sha(repo: Repository) -> Option<(String, String)> {
match repo.head().ok() {
Some(reference) => {
match (reference.name(), reference.target()){
(Some(branch), Some(sha)) => Some((branch.to_string(), sha.to_string())),
_ => None
}
},
None => None
}
}
The error that arises is E0061, and I think it's because the 'value' in the Option returned from branch_and_sha() is a tuple. branch_and_sha() effectively says, "If there is a repository, get it's reference, and if that exists, if it has both a name (branch) and target (sha), return an Option<(String, String)> with that info - otherwise return None. And the reporting function wants to do something if all of the Repository, branch and sha can be found - and nothing otherwise. (It shouldn't error or panic.)
To some degree this is contrived - it's an example of an optimistic reporting function similar to several I'd like to write. I'm looking for a clean, idiomatic way to do it. The key thrust is 'several depths and several branches could return None which should cause a no-op, and otherwise make specific (leaf) info available.' The specific error is how I should be handling the and_then function, which is surprisingly difficult to find similar problems about.
First off, you have a minor typo. Closures in Rust don't use =>. So your closure should look more like
.and_then(|branch_sha| { // Note: No => here
let (branch, sha) = branch_sha;
println!("Branch={} sha={}", branch, sha);
None
});
Then the error we get is
--> so_cleanly.rs:15:10
|
15 | .and_then(|branch_sha| {
| ^^^^^^^^ cannot infer type for type parameter `U` declared on the associated function `and_then`
|
and_then is declared with two generic arguments: U and F (technically, there's also T, but that's determined by the type of the receiver self, so we won't worry about it). Now, F is the type of the closure and is always determined by the argument. On the other hand, U is the return type of the closure.
The closure must return an Option<U>. Rust needs to look at the closure and determine what its return type is. What does the closure return? It returns None, and None can be Option<U> for any U in existence. Rust doesn't know which one to use. We need to tell it. We could do that on the line we return None from
None as Option<()>
or in the and_then call itself.
.and_then::<(), _>(|branch_sha| { ... })
However, the compiler is making a very valid point. and_then and company produce a result of type Option, which you're ignoring. You're writing a piece of code which has side effects and doesn't produce a value, which is sensible, but you're using a functional interface intended for returning values. It can be done, but it's probably not idiomatic. I had to look at your code a few times before realizing that the () return value was not a typo.
One option is to return Option<()> from your report_repo. The () on the inside indicates that we don't care about anything except the side effects, and the Option lets the caller of report_repo handle (or ignore) any errors that occur during the process, whereas your current function simply suppresses all errors unconditionally.
fn report_repo() -> Option<()> {
Repository::open(".")
.ok()
.and_then(branch_and_sha)
.map(|branch_sha| {
let (branch, sha) = branch_sha;
println!("Branch={} sha={}", branch, sha);
// Implicit return of () here, which we could do explicitly if we wanted
})
}
I've made several subtle changes here. The return type is Option<()> now. In accordance with that, there's no semicolon at the end of the line inside the function (we're returning that value). Finally, the last and_then is a map, since the final step can't fail and simply does some work on Some.
That's an improvement, but it's probably still not how I'd write this function.
Instead, if you're performing code for side effects, consider using the ? operator, which does and_then and map shenanigans but keeps the control flow relatively linear. and_then and its friends are great for constructing values, but the point of your function is that it should read like a sequence of instructions, not a constructor for a value. This is how I would write that function.
fn report_repo() -> Option<()> {
let repo = Repository::open(".").ok()?;
let (branch, sha) = branch_and_sha(repo)?;
println!("Branch={} sha={}", branch, sha);
Some(())
}
Each line that ends in a ? effectively says "If this thing is None, return None now. Otherwise, keep going." But a cursory glance of the code reads "open the repo, branch and sha, and then print", which is exactly what you want people to see at a glance.
If we wanted to be really proper about this, we should probably return Result<(), Error>, where Error is some more detailed error type, but that's overkill for this simple example snippet.
You can chose an if let style too, you do not need the option value so just stop using them at some point it feels more comfortable:
fn report_repo() {
if let Some((branch, sha)) = Repository::open(".").ok().and_then(branch_and_sha) {
println!("Branch={} sha={}", branch, sha);
}
}

How can I make this Rust code more idiomatic

Recently I started to learn Rust and one of my main struggles is converting years of Object Oriented thinking into procedural code.
I'm trying to parse a XML that have tags that are processed by an specific handler that can deal with the data it gets from the children.
Further more I have some field members that are common between them and I would prefer not to have to write the same fields to all the handlers.
I tried my hand on it and my code came out like this:
use roxmltree::Node; // roxmltree = "0.14.0"
fn get_data_from(node: &Node) -> String {
let tag_name = get_node_name(node);
let tag_handler: dyn XMLTagHandler = match tag_name {
"name" => NameHandler::new(),
"phone" => PhoneHandler::new(),
_ => DefaultHandler::new()
}
if tag_handler.is_recursive() {
for child in node.children() {
let child_value = get_data_from(&child);
// do something with child value
}
}
let value: String = tag_handler.value()
value
}
// consider that handlers are on my project and can be adapted to my needs, and that XMLTagHandler is the trait that they share in common.
My main issues with this are:
This feels like a Object oriented approach to it;
is_recursive needs to be reimplemented to each struct because they traits cannot have field members, and I will have to add more fields later, which means more boilerplate for each new field;
I could use one type for a Handler and pass to it a function pointer, but this approach seems dirty. e.g.:=> Handler::new(my_other_params, phone_handler_func)
This feels like a Object oriented approach to it
Actually, I don't think so. This code is in clear violation of the Tell-Don't-Ask principle, which falls out from the central idea of object-oriented programming: the encapsulation of data and related behavior into objects. The objects (NameHandler, PhoneHandler, etc.) don't have enough knowledge about what they are to do things on their own, so get_data_from has to query them for information and decide what to do, rather than simply sending a message and letting the object figure out how to deal with it.
So let's start by moving the knowledge about what to do with each kind of tag into the handler itself:
trait XmlTagHandler {
fn foreach_child<F: FnMut(&Node)>(&self, node: &Node, callback: F);
}
impl XmlTagHandler for NameHandler {
fn foreach_child<F: FnMut(&Node)>(&self, _node: &Node, _callback: F) {
// "name" is not a recursive tag, so do nothing
}
}
impl XmlTagHandler for DefaultHandler {
fn foreach_child<F: FnMut(&Node)>(&self, node: &Node, callback: F) {
// all other tags may be recursive
for child in node.children() {
callback(child);
}
}
}
This way you call foreach_child on every kind of Handler, and let the handler itself decide whether the right action is to recurse or not. After all, that's why they have different types -- right?
To get rid of the dyn part, which is unnecessary, let's write a little generic helper function that uses XmlTagHandler to handle one specific kind of tag, and modify get_data_from so it just dispatches to the correct parameterized version of it. (I'll suppose that XmlTagHandler also has a new function so that you can create one generically.)
fn handle_tag<H: XmlTagHandler>(node: &Node) -> String {
let handler = H::new();
handler.foreach_child(node, |child| {
// do something with child value
});
handler.value()
}
fn get_data_from(node: &Node) -> String {
let tag_name = get_node_name(node);
match tag_name {
"name" => handle_tag::<NameHandler>(node),
"phone" => handle_tag::<PhoneHandler>(node),
_ => handle_tag::<DefaultHandler>(node),
}
}
If you don't like handle_tag::<SomeHandler>(node), also consider making handle_tag a provided method of XmlTagHandler, so you can instead write SomeHandler::handle(node).
Note that I have not really changed any of the data structures. Your presumption of an XmlTagHandler trait and various Handler implementors is a pretty normal way to organize code. However, in this case, it doesn't offer any real improvement over just writing three separate functions:
fn get_data_from(node: &Node) -> String {
let tag_name = get_node_name(node);
match tag_name {
"name" => get_name_from(node),
"phone" => get_phone_from(node),
_ => get_other_from(node),
}
}
In some languages, such as Java, all code has to be part of some class – so you can find yourself writing classes that don't exist for any other reason than to group related things together. In Rust you don't need to do this, so make sure that any added complication such as XmlTagHandler is actually pulling its weight.
is_recursive needs to be reimplemented to each struct because they traits cannot have field members, and I will have to add more fields later, which means more boilerplate for each new field
Without more information about the fields, it's impossible to really understand what problem you're facing here; however, in general, if there is a family of structs that have some data in common, you may want to make a generic struct instead of a trait. See the answers to How to reuse codes for Binary Search Tree, Red-Black Tree, and AVL Tree? for more suggestions.
I could use one type for a Handler and pass to it a function pointer, but this approach seems dirty
Elegance is sometimes a useful thing, but it is subjective. I would recommend closures rather than function pointers, but this suggestion doesn't seem "dirty" to me. Making closures and putting them in data structures is a very normal way to write Rust code. If you can elaborate on what you don't like about it, perhaps someone could point out ways to improve it.

Resources