I have this rust method error message:
error[E0277]: the `?` operator can only be used on `Option`s,
not `Result`s, in an async function that returns `Option`
I must admit, I often encounter Rust error messages which appear confusing to me, while to most other coders they make absolute sense.
So, I apologize in advance for posting this question.
First of all: what does that second comma in the error message mean? Should I read it as the following:
"If an async function call [within another function] returns an enum of the type Result then the ? operator can only be applied if, and only if, the respective [other] function also returns an enum of type Result and not an enum of type Option"
Pardon my verbose language. I hope I got my point across.
What also got me confused was the error message with that very same Reference i.e. error[E0277] , which is listed in the official rust error codes index, states:
"You tried to use a type which doesn't implement some trait in a place which expected that trait."
In which universe do these two error messages have anything in common, except for the identical reference number?
And here's the entire error block, which Rust produced:
error[E0277]: the `?` operator can only be used on `Option`s, not `Result`s, in an async function that returns `Option`
--> src/utils/tokenizer.rs:72:73
|
70 | pub async fn clear(&self) -> Option<String> {
| _________________________________________________-
71 | | let mut conn = self.pool.get().await.unwrap();
72 | | let mut iter: redis::AsyncIter<i32> = conn.sscan("my_set").await?;
| | ^ use `.ok()?` if you want to discard the `Result<Infallible, Red
Error>` error information
73 | | while let Some(element) = iter.next_item().await {
... |
79 | | Some(String::from("A"))
80 | | }
| |_____- this function returns an `Option`
|
= help: the trait `FromResidual<Result<Infallible, RedisError>>` is not implemented for `std::option::Option<std::string::String>`
= help: the following other types implement trait `FromResidual<R>`:
<std::option::Option<T> as FromResidual<Yeet<()>>>
<std::option::Option<T> as FromResidual>
For more information about this error, try `rustc --explain E0277`.
What is the canonical error message, the one from the error code index page or the one, the compiler produces?
Your function clear returns Option<String> but the function conn.sscan("my_set").await?; returns a Result. You cannot use ? to propagate the result because that doesn't match the return type of clear(&self) -> Option<String>.
You will need explicitly handle the Result or convert it to an Option<String>. You could also try adding .ok()? per the compiler hint and see what that gets you.
Whatever you pick the important thing is just to make sure that the return type matches what your ? operator is unwrapping.
Yes, your interpretation is correct, although to be pedantic the error says the opposite:
"If an async function returns Option then the ? operator can only be applied to expressions that has the type Option, and not Result".
And if you ask why on earth this has error code E0277? Well, this is because in order to be propagated using ? a type has to implement the (experimental) Try trait with some combination of type parameters and associated types corresponding to some other combination of the return type of the function. So, in essence, an incompatible type in ? boils down to an unsatisfied trait bound. But unless you're interested in the inner workings of ?, this is not really important.
Related
I am just starting to learn Rust. The entry method an Entry enum, which can be accessed via the match function. But then I don't get why it doesn't compile and it panics.
Can someone explain please what I'm understanding wrong?
let mut scores: HashMap<String, u32> = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
let a = 10;
match scores.entry("poneyland".to_string()) {
Occupied(o) => o.remove_entry(),
Vacant(o) => o.insert(37),
}
But then I don't get why it doesn't compile and it panics.
It can't both fail to compile and panic, since panic is a runtime behaviour. Either way, when there is an error it is usually a good idea to post it, as that makes diagnostic much easier, especially in Rust which generally has pretty helpful error messages.
In this case, the compilation error tells you exactly what's what:
error[E0308]: `match` arms have incompatible types
--> src/main.rs:15:22
|
13 | / match scores.entry("poneyland".to_string()) {
14 | | Occupied(o) => o.remove_entry(),
| | ---------------- this is found to be of type `(String, u32)`
15 | | Vacant(o) => o.insert(37),
| | ^^^^^^^^^^^^ expected tuple, found `&mut u32`
16 | | }
| |_____- `match` arms have incompatible types
|
= note: expected tuple `(String, u32)`
found mutable reference `&mut u32`
You pretty much just need to read the message, and possibly the expanded document.
What it boils down to is a rust match is an expression, meaning it has a value of a specific type, meaning all arms must have values of the same type (or a compatible type e.g. !).
Here, as the compiler error tells you, in one arm you're calling remove_entry which returns a tuple of (key, value), while in the other arm you're calling insert which returns a mutable reference to the inserted value.
These types are obviously not compatible.
The easiest way to fix this is to make them compatible by... suppressing the results entirely since you're not using them: convert the arm expressions to arm blocks and terminate the expressions with ;. That makes the expressions into statements and suppresses their value, implicitly returning () instead:
match scores.entry("poneyland".to_string()) {
Occupied(o) => {
o.remove_entry();
}
Vacant(o) => {
o.insert(37);
}
}
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=1b885daa2a4eaa6d9d7c366753f6c2aa
I've seen this pattern on more than one occasion:
fn f() -> Result<..., ...> {
...
Ok(expression()?)
}
Specifically, I find the order Ok(expression()?) confusing. What's the return type of expression()? and is there an interplay between Ok and this type? It seems, the function Ok must do more than just capturing the value. But how can it force a return with an error, if it's the last expression wrapping the return type of expression()?.
The ? operator either returns (as in the return statement) the error variant, or extracts the value from the Ok variant. Thus Ok(expression()?) is more or less equal to:
// This pattern is so common in rust, that the language designers
// introduced the `?` to reduce the amount of boilerplate needed
let result = expression();
let r = match result{
Err(e) => return Err(e.into()), // tries to convert the error into the required type if necessary (and if possible)
Ok(r) => r
}
Ok(r)
So basically Ok(expression()?) can be simplified to just
expression() if its Err variant matches the one from the function definition.
Historical context:
Extracting the Ok() variant and propagating the Err variant is a very common pattern in rust. In order to reduce the amount of boilerplate code, the rust team introduced the try!() macro; But it's so clumsy and does not really work well with method chaining. The rust team introduced the ? operator, which is doing absolutely the same thing, except for it's not so intrusive as try!()
? just leverages the error returned by expression if it exists or unwraps the value from within the returned Ok if not. Ok(expression()?) could be just expresion() and clippy would warn you about that:
warning: question mark operator is useless here
--> src/lib.rs:8:5
|
8 | Ok(expression()?)
| ^^^^^^^^^^^^^^^^^ help: try: `expression()`
|
= note: `#[warn(clippy::needless_question_mark)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
Playground
You can follow the documentation for further information
As per the comments. Notice that ? is useful if the error types differ but you have an implementation of From between error types or you use Box<dyn Error> as return type.
In which case you can either, bubble up the error if it is an error, otherwise rewrap the value with an Ok
Ok(expression()?)
or, map your error type into the correct error type with map_err
expression.map_err(|e| ...)
I tried some code like this:
fn main() {
let a = vec!["May", "June"];
let s = a.iter().fold("", |s2, s3|
s2 + s3
);
println!("{}", s == "MayJune");
}
Result:
error[E0369]: cannot add `&&str` to `&str`
--> a.rs:4:10
|
4 | s2 + s3
| -- ^ -- &&str
| | |
| | `+` cannot be used to concatenate two `&str` strings
| &str
|
help: `to_owned()` can be used to create an owned `String` from a string
reference. String concatenation appends the string on the right to the string
on the left and may require reallocation. This requires ownership of the string
on the left
|
4 | s2.to_owned() + s3
| ^^^^^^^^^^^^^
Ok, fair enough. So I change my code to exactly that. But then I get this:
error[E0308]: mismatched types
--> a.rs:4:7
|
4 | s2.to_owned() + s3
| ^^^^^^^^^^^^^^^^^^
| |
| expected `&str`, found struct `std::string::String`
| help: consider borrowing here: `&(s2.to_owned() + s3)`
Ok, fair enough. So I change my code to exactly that. But then I get this:
error[E0515]: cannot return reference to temporary value
--> a.rs:4:7
|
4 | &(s2.to_owned() + s3)
| ^--------------------
| ||
| |temporary value created here
| returns a reference to data owned by the current function
Why is Rust giving bogus suggestion, and is what I am trying to do possible? Note, I would prefer to avoid suggestions such as "just use join" or similar, as this question is intended to address a more generic problem. Rust version:
rustc 1.46.0 (04488afe3 2020-08-24)
is what I am trying to do possible?
Stargazeur provided a working version in their comment: the initial value / accumulator needs to be a String rather than an &str.
Why is Rust giving bogus suggestion
Rustc doesn't have a global-enough vision so it is able to see the "detail" issue but it doesn't realise that it's really a local effect of a larger problem: fold's signature is
fn fold<B, F>(self, init: B, f: F) -> B
because you're giving fold an &str, it must ultimately return an &str, which is only possible if F just returns something it gets from "the outside", not if it creates anything internally. Since you want to create something inside your callback, the value of init is the issue.
Rustc doesn't see the conflict at that level though, because as far as it's concerned that's a perfectly valid signature e.g. you might be following a chain of things through a hashmap of returning a constant string reference for all it cares, the only real conflict it sees is between this:
F: FnMut(B, Self::Item) -> B
and the implementation of your function which doesn't actually work, so it tries to help you with that:
Rust doesn't allow adding two &str together because that would implicitly allocate a String which is the sort of hidden concern the core team would rather not hide, so Add is only implemented between String and &str, that's the first issue you see, and since that's somewhat unusual (the average language just lets you concatenate string-ish stuff or even not-at-all-strings to strings) rustc devs have added a help text noting that the LHS must be an owned String, which generally helps / works but
then the addition returns a String, so now your function doesn't match the F signature anymore: since the init is an &str that's the type of the accumlator so you need to return an &str
except if you try to create a reference to the string you've just created, you just created it inside the function, once the function returns the string will be dead and the reference left dangling, which rust can not allow
And that's how despite the best intentions, because the compiler's view is too local it guilelessly leads you down a completely useless path of frustration.
You may want to report this issue on the bug tracker (or see if it's already there). I don't know if the compiler diagnostics system would be able to grok this situation though.
I have a piece of code like this:
use std::cell::RefCell;
use std::rc::Rc;
struct A(bool);
impl A {
fn get_ref(&self) -> &Rc<RefCell<bool>> {
&Rc::new(RefCell::new(self.0))
}
fn copy_ref(&self) -> &Rc<RefCell<bool>> {
Rc::clone(self.get_ref())
}
}
fn main() {
let a = A(true);
a.copy_ref();
}
and I received warning complaining about the Rc::clone function not getting a reference:
error[E0308]: mismatched types
--> src/main.rs:12:9
|
12 | Rc::clone(self.get_ref())
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected reference, found struct `std::rc::Rc`
|
= note: expected type `&std::rc::Rc<std::cell::RefCell<bool>>`
found type `std::rc::Rc<std::cell::RefCell<bool>>`
I have been working on this all night but I cannot get it to work. The method get_ref is already typed as returning &Rc<RefCell<bool>>, but why would the compiler give the error?
The error is not talking about the argument you put into Arc::clone(), but the whole expression Rc::clone(...) which has a different type (Rc<...>) than the return type of your function (&Rc<...>).
If you were passing a wrong argument to Rc::clone, it would like look this:
--> src/main.rs:13:19
|
13 | Rc::clone(false)
| ^^^^^ expected reference, found bool
|
= note: expected type `&std::rc::Rc<_>`
found type `bool`
So the naive way to fix the type error is to write &Rc::clone(...) instead. Then the last expression of your function has the same type as your function's declared return type. But as you will notice, you will get other errors afterwards.
Let's take a step back to see that your approach is flawed here. For the most important point, please see "Is there any way to return a reference to a variable created in a function?". Spoiler: you really don't want to. So constructs like your get_ref() just don't make sense, as you return a reference to a variable you create inside your function (a variable of type Rc).
In your case the direct solution is probably pretty simple: just remove the reference. Rc<T> is already a pointer/reference type, so there is no need (in general) to have a reference to it.
However, since you are using Rc, you are probably interested in reference counting. So in that case, you probably shouldn't create a new Rc every time the function is called. Otherwise you could end up with a bunch of Rcs with reference count 1, which is not really the point. So instead, your type A should already store an Rc<RefCell<bool>>.
But all I'm doing here is guessing what you actually want to do which is not clear from your question. Maybe you can ask a different question, or add the information to this question, or explain this in the comments.
This question already has answers here:
Why do try!() and ? not compile when used in a function that doesn't return Option or Result?
(4 answers)
Closed 5 years ago.
use std::io;
use std::fs::File;
use std::io::prelude::*;
fn main() {
let mut csv = File::open("Item.csv")?;
}
this is some part of my code, and I've got error:
Compiling eracsv v0.1.0 (file:///C:/Users/jwm/Project/eracsv)
error[E0277]: the trait bound `(): std::ops::Try` is not satisfied
--> src\main.rs:
|
| let mut csv = File::open("Item.csv")?;
| -----------------------
| |
| the `?` operator can only be used in a function that returns `Result` (or another type that implements `std::ops::Try`)
| in this macro invocation
|
= help: the trait `std::ops::Try` is not implemented for `()`
= note: required by `std::ops::Try::from_error`
I have deal with both rustc 1.19 stable and 1.22 nightly and both discharging same error.
but, this is exactly same code as rust doc, isn't it? It is explicitly mentioned that File::open() function returns result .
I am curious why ? operator makes compile error while unwrap() doesn't.
What the error message is actually telling you is you're using the ? operator inside of a function that returns () (this being the main function). The ? operator propagates errors upwards, but it can only do that if the function it's used in actually has a compatible return type that can represent that error.
Or in other words, you can only use it in a function that itself is returning a Result (with a compatible error type). Your main does not return a Result, so you cannot use the ? operator inside of it.
You may also be interested in RFC 1937, which would allow the use of ? in main (by allowing you to declare that main returns a Result).