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

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.

Related

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

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

How to handle error in unwrap() function? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
What is the best way to handle the error when reading arguments from the CLI?
For example, when the second argument is not provided in the following code?
use std::env;
fn main() {
let filename = env::args().nth(0).unwrap();
let other_argument = env::args().nth(1).unwrap();
}
I would recommend to use the match to differentiate between the value of enums.
An example which illustrates this approach for Options like you might need it for your arguments (you could also use different approaches) and Result
use std::env;
use std::fs;
fn main() {
// match 1
// handle Options https://doc.rust-lang.org/std/option/enum.Option.html using match
match env::args().nth(1) {
Some(v) => {
println!("arg 1: {:?}", v);
// match 2
// handle Result https://doc.rust-lang.org/std/result/ using match
match fs::read_to_string(&v) {
Ok(contents) => println!("{}", contents),
Err(e) => println!("{}", e),
};
}
None => {
println!("you have not passed an argument.");
}
}
}
Like already others wrote, I can also really recommend the crate https://docs.rs/structopt/latest/structopt/ in order to evaluate command line arguments.
I imagine that if the user does not input those arguments then you probably want the program to crash.
You can use expect which is a nice alternative to unwrap because it lets you specify the error message when crashing.
let filename = env::args().nth(0).expect("Did not provide the filename argument");
https://learning-rust.github.io/docs/e4.unwrap_and_expect.html
If you want the "best" way to deal with this, I recomment using the structopt library for handling program arguments.
https://lib.rs/crates/structopt
Fyi: not sure if this is your intended behavior but the 0th argument is actually the name of the program being executed rather than the first argument on the command line.

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);
}
}

My function could return a promise or value, how to avoid it? [duplicate]

This question already has answers here:
How to maintain a promise-like API in this case?
(2 answers)
Closed 6 years ago.
I think it's bad practice to have different return types. So, this is my function, and I want it to always return a promise.
I tried to simplify the scenario. Let's say I have two lines of products (line X and line Y) and the way I'm retrieving a product by name, from each line is totally different from the other.
Please also note that ideally, I wanted to have a function to "or" two promises and return whichever that resolves successfully. But I couldn't think of a better way to achieve this!
ProductService.findProductByName = function findProductByName(name) {
return LineXService.findOneByName(name) // promise
.then(function _returnProduct(product) {
return product
? product // value
: LineYService.findOneByName(name)); // promise
})
};
You're right that returning a consistent thing is better than an ambiguous result. Normally you can cast an arbitrary value into a promise easily enough. For example using resolve:
if (product) {
return Promise.resolve(product);
}
return LineYService.findOneByName(name);
It's worth noting, as Kevin B observes, that this is irrelevant in this particular case since your outer function always returns a promise. The need to promisify a value is only a concern if this is not part of a surrounding promise.

Resources