I'm new to rust (coming from programming in c/c++ and python) so to learn I'm writing some basic functions. Below I have a factorial function that takes in a signed integer and has two if checks for it.
fn factorial(x: i32) -> i32 {
let result = if x > 1 {
x * factorial(x-1)
} else if x <= 1 {
1
};
result
}
To my knowledge, the if and else-if blocks should handle every case for it. However, when compiling it throws the following error:
error[E0317]: `if` may be missing an `else` clause
--> src/main.rs:22:12
|
22 | } else if x <= 1 {
| ____________^
23 | | 1
| | - found here
24 | | };
| |_____^ expected `()`, found integer
|
= note: `if` expressions without `else` evaluate to `()`
= help: consider adding an `else` block that evaluates to the expected type
error: aborting due to previous error
For more information about this error, try `rustc --explain E0317`.
error: could not compile `functions`
If I replace the else-if with just an else, it compiles just fine. Why do I need to replace it with an else? Shouldn't the previous else-if be good enough?
As the error message says, if an if expression doesn't have an else then the type of the expression is (). This is because the expression can only have one type, and there is no sensible default value in the general case for if the condition evaluated to false - that's what else is for!
In your case, the compiler could have figured out that the two predicates are in fact exhaustive. It doesn't, and that really is just how it is. If the compiler could detect exhaustiveness in this case it would be weird if it couldn't also detect it in other "obvious" cases. But predicates can be arbitrary expressions and it would be impossible to implement the check in the general case.
In this example, the compiler would have to analyse the body of the random function to know if the predicates are exhaustive or not:
// random(x) might return a different value each time it's called
if random(x) > 1 {
1
} else if random(x) <= 1 {
2
} // Not exhaustive!
Being consistent seems like the best choice for the language, since you can always add an else at the end.
Related
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.
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| ...)
The following code appears to be trivial and unambiguous (Playground):
let a: u16 = 5;
let b: u32 = 10;
let c = a as u32 < b;
Yet the compiler (as of 2017-05-30) fails with a syntax error:
error: expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `;`
--> src/main.rs:6:25
|
6 | let c = a as u32 < b;
|
What is wrong with the compiler?
Note: The latest Rust compilers now provide a more useful error message (#42578):
error: `<` is interpreted as a start of generic arguments for `u32`, not a comparison
--> src/main.rs:6:22
|
6 | let c = a as u32 < b;
| -------- ^ -- interpreted as generic arguments
| | |
| | not interpreted as comparison
| help: try comparing the casted value: `(a as u32)`
This is a known compiler issue (#22644). Simply put, since a type (u32) was followed by <, the compiler attempted to parse < as the beginning of a type parameter list. Hence, the compiler was expecting something like u32 < b >, which would be syntactically valid, even though it doesn't make sense. However, an example that makes perfectly valid Rust is foo as Rc < fmt::Debug >, and if the syntax was too eager to make < the less-than operator, this one would fail just the same.
Of course, technically there are ways around it: C++ and C# have had the same ambiguity from the beginning, they just happen to have a combination of means to disambiguate these cases:
different syntax;
symbol tables at the parser level;
or some form of look-ahead.
Any of these will either have implications in the complexity of the parser or in the complexity of the language's syntax definition. The inclusion of those mechanisms in Rust could lead to breaking changes in the syntax (or probably just the rustc syntax crate).
As there is currently no active discussion to address this issue, a fairly simple and long-term solution is to wrap the cast around parentheses:
let c = (a as u32) < b;
During work on a side project I've tried to use an increment operator, as following:
fn main() {
let mut my_var = 5;
my_var++;
}
and received the following error:
error: expected expression, found `+`
--> src\main.rs:3:12
|
3 | my_var++;
| ^
What's wrong with my code?
Increment (++) and decrement (--) operators are not supported in Rust.
From Rust's FAQ:
Why doesn't Rust have increment and decrement operators?
Preincrement and postincrement (and the decrement equivalents), while
convenient, are also fairly complex. They require knowledge of
evaluation order, and often lead to subtle bugs and undefined behavior
in C and C++. x = x + 1 or x += 1 is only slightly longer, but
unambiguous.