What does the '#' symbol do in Rust? - rust

I forgot to specify the type of a parameter and the error message was as follows:
error: expected one of `:` or `#`, found `)`
--> src/main.rs:2:12
|
2 | fn func(arg)
| ^ expected one of `:` or `#` here
Which raises the question: what can you do with an # symbol? I don't remember reading about using the # symbol for anything. I also did some Googling and couldn't find anything. What does # do?

You can use the # symbol to bind a pattern to a name. As the Rust Reference demonstrates:
let x = 1;
match x {
e # 1 ... 5 => println!("got a range element {}", e),
_ => println!("anything"),
}
Assignments in Rust allow pattern expressions (provided they are complete) and argument lists are no exception. In the specific case of #, this isn't very useful because you can already name the matched parameter. However, for completeness, here is an example which compiles:
enum MyEnum {
TheOnlyCase(u8),
}
fn my_fn(x # MyEnum::TheOnlyCase(_): MyEnum) {}

Related

Matching against multiple Boxed values

I have the following code which compiles fine
#[derive(Debug, PartialEq, Clone)]
pub enum Expression {
Const(i32),
Neg(Box<Expression>),
Add(Box<Expression>, Box<Expression>),
}
fn simplify(expr: &Expression) -> Expression {
match expr {
Expression::Neg(x) => match **x {
Expression::Const(n) => Expression::Const(-n),
_ => expr.clone()
},
// GIVES ERROR
// Expression::Add(x, y) => match (**x, **y) {
// (Expression::Const(n), Expression::Const(m)) => Expression::Const(n + m),
// _ => expr.clone()
// },
Expression::Add(x, y) => match **x {
Expression::Const(n) => match **y {
Expression::Const(m) => Expression::Const(n + m),
_ => expr.clone()
}
_ => expr.clone()
}
_ => expr.clone()
}
}
But if I replace the Expression::Add arm with the commented out version, I get the following compiler error
error[E0507]: cannot move out of `**x` which is behind a shared reference
--> src/lib.rs:21:41
|
21 | Expression::Add(x, y) => match (**x, **y) {
| ^^^ move occurs because `**x` has type `Expression`, which does not implement the `Copy` trait
error[E0507]: cannot move out of `**y` which is behind a shared reference
--> src/lib.rs:21:46
|
21 | Expression::Add(x, y) => match (**x, **y) {
| ^^^ move occurs because `**y` has type `Expression`, which does not implement the `Copy` trait
For more information about this error, try `rustc --explain E0507`.
Is there a reason we may match against the lone **x but not in a tuple like (**x, **y)? Is the former actually being converted or hiding some syntactic sugar? Is there a simpler way of writing this Add arm than with the two nested matches?
EDIT: I also see that there is a ref keyword, which is supposed to address something like this, but changing my tuple match expression to (ref **x, ref **y) gives a syntax error (error: expected expression, found keyword ref).
TL;DR: Match against (&**x, &**y).
What's going on here is interesting. And the TL;DR is: when you match v {}, you don't read v. You create a place for v.
A place is something we can read from. Or write to. Or do nothing with. The important point is that creating place alone does not involve such operation. You can read/write it later, but when you create it, it is just a place.
In your match, the type of x and y is &Box<Expression>. When we match **x, we don't read x. Thus, we don't move **x either. What we do is create a place for **x. We then match this place against Expression::Const(n). Now we read x and extract n out of it. But n is i32 - Copy - so this is fine.
In contrast, when you use a tuple (**x, **y), since you don't match against **x and **y directly you do read them. And because you read them, and they are not Copy (Expression), you move out of them. Now this is an error because you cannot move out of shared references. You match them after, but they're already moved.
You can try match(x.as_ref(),y.as_ref())

Why does the order of parameters matter when using the matches! macro?

I'm trying to compare a variable of enum type stored in a vector of structs, with a variable of the same type passed into my function as a parameter. So, both enums are stored in variables. However, I'm getting unexpected results. I'm using the matches!() macro for comparison. Can anyone explain this behaviour?
enum Foo {
A,
B,
}
fn main() {
let a = Foo::A;
if matches!(a, Foo::A) { println!("expected") }
if matches!(a, Foo::B) { println!("not expected 1") }
if matches!(Foo::B, a) { println!("not expected 2") }
let b = Foo::B;
if matches!(a, b) { println!("not expected 3") }
}
Output:
expected
not expected 2
not expected 3
The matches! macro is not symmetrical: the first operand is the expression to be tested and the second operand is a pattern to try matching the first operand against. If this order is not followed, the results will likely be unexpected.
While the first two matches! are well formed and do what you expect, the third and fourth one are most likely not what you want.
matches!(Foo::B, a) // test `Foo::B` against pattern `a`
matches!(a, b) // test `a` against pattern `b`
The expression to be tested is the literal value Foo::B, and the pattern is a new identifier, a in the third example and b in the fourth one. Since the pattern is only an identifier, it will match any expression. It does not relate at all to the variables a and b declared beforehand. The code below would still compile even when b did not exist.
let a = Foo::A;
matches!(a, b);
Note also that these if statements would print warnings, because a new variable was created from the pattern, but was not used.
warning: unused variable: `a`
--> src/main.rs:7:25
|
7 | if matches!(Foo::B, a) { println!("not expected 2") }
| ^ help: if this is intentional, prefix it with an underscore: `_a`
|
= note: `#[warn(unused_variables)]` on by default
See also:
Why does match not work while PartialEq/Eq works fine?
Why is this match pattern unreachable when using non-literal patterns?
Thank you to E_net4 the curator for the helpful answer. I can now get the behaviour I expect using the following:
#[derive(PartialEq)]
enum Foo {
A,
B,
}
fn main() {
let a = Foo::A;
let b = Foo::B;
let s = match a {
n if n == b => "a matches b",
n if n == a => "a matches a",
_ => "no match"
};
println!("{}", s);
}
With output:
a matches a

How to extract value from option (Solving hashmap2 problem from rustlings) [duplicate]

I am fairly new to Rust and cannot get my head around this confusing error.
I am simply trying to match on an Option returned by the get function of a HashMap. If a value is returned I want to increment it, if not I want to add a new element to the map.
Here is the code:
let mut map = HashMap::new();
map.insert("a", 0);
let a = "a";
match map.get(&a) {
Some(count) => *count += 1,
None => map.insert(a, 0),
}
The resulting error:
error[E0308]: match arms have incompatible types
--> <anon>:7:5
|
7 | match map.get(&a) {
| _____^ starting here...
8 | | Some(count) => *count += 1,
9 | | None => map.insert(a, 0),
10 | | }
| |_____^ ...ending here: expected (), found enum `std::option::Option`
|
= note: expected type `()`
found type `std::option::Option<{integer}>`
note: match arm with an incompatible type
--> <anon>:9:17
|
9 | None => map.insert(a, 0),
| ^^^^^^^^^^^^^^^^
I am not really sure what types the compiler is complaining about here, as both Some and None are both part of the same enum type. Can anyone explain what issue the compiler is having with my code?
The compiler is referring to the value the match arm bodies return, not the type of the pattern of each match arm.
Some(count) => *count += 1,
None => map.insert(a, 0),
The expression *count += 1 evaluates to () (called "unit" in Rust, "void" in many other languages). The expression map.insert(a, 0) on the other hand returns Option<V> where V is the value type of the hash map (an integer in your case). Suddenly the error message does make some sense:
= note: expected type `()`
= note: found type `std::option::Option<{integer}>`
I suppose you don't even want to return something from the match block (remember: match blocks are expressions, too, so you could return something from it). To discard the result of any expression, you can convert it to a statement with ;. Let's try this:
match map.get(&a) {
Some(count) => {
*count += 1;
}
None => {
map.insert(a, 0);
}
}
Each match arm body is a block now (something between { and }) and each block contains one statement. Note that we technically don't need to change the first match arm, as *count += 1 already returns (), but this way it's more consistent.
But once you test this, another error related to borrowing will be shown. This is a well known issue and is explained in more detail here. In short: the borrow checker is not smart enough to recognize that your code is fine and therefore you should use the super nice entry-API:
let map = HashMap::new();
map.insert("a", 0);
let a = "a";
*map.entry(&a).or_insert(0) += 1;

Why do I get the error "expected integral variable, found Option" when matching on an integer?

I am trying to use match in Rust. I wrote a function:
fn main() {
let try = 3;
let x = match try {
Some(number) => number,
None => 0,
};
}
But I'm getting the error:
error[E0308]: mismatched types
--> src/main.rs:4:9
|
4 | Some(number) => number,
| ^^^^^^^^^^^^ expected integral variable, found enum `std::option::Option`
|
= note: expected type `{integer}`
found type `std::option::Option<_>`
error[E0308]: mismatched types
--> src/main.rs:5:9
|
5 | None => 0,
| ^^^^ expected integral variable, found enum `std::option::Option`
|
= note: expected type `{integer}`
found type `std::option::Option<_>`
I tried something like let try: i32 = 3; to make sure that try is an integral value, but I still get the same error.
I think you want this:
fn main() {
let try = Some(3);
let x = match try {
Some(number) => number,
None => 0,
};
}
The issue is that you're trying to match an integer against Some(...) and None, which are Options. This doesn't really make sense... an integer can never be None.
Instead, I think you want to use the type Option<i32> and convert it to an i32 by using a default value. The above code should accomplish that. Note that if that's all you're trying to do, this is an easier way:
let x = try.unwrap_or(0);
In match expressions the type of the value you are matching on must correspond to the variants in the block following it; in your case this means that try either needs to be an Option or the match block needs to have integral variants.
I highly recommend reading The Rust Book; Rust is strongly typed and this is one of the most basic concepts you will need to familiarize yourself with.

Error thrown citing match arms with incompatible types when pattern matching an Option

I am fairly new to Rust and cannot get my head around this confusing error.
I am simply trying to match on an Option returned by the get function of a HashMap. If a value is returned I want to increment it, if not I want to add a new element to the map.
Here is the code:
let mut map = HashMap::new();
map.insert("a", 0);
let a = "a";
match map.get(&a) {
Some(count) => *count += 1,
None => map.insert(a, 0),
}
The resulting error:
error[E0308]: match arms have incompatible types
--> <anon>:7:5
|
7 | match map.get(&a) {
| _____^ starting here...
8 | | Some(count) => *count += 1,
9 | | None => map.insert(a, 0),
10 | | }
| |_____^ ...ending here: expected (), found enum `std::option::Option`
|
= note: expected type `()`
found type `std::option::Option<{integer}>`
note: match arm with an incompatible type
--> <anon>:9:17
|
9 | None => map.insert(a, 0),
| ^^^^^^^^^^^^^^^^
I am not really sure what types the compiler is complaining about here, as both Some and None are both part of the same enum type. Can anyone explain what issue the compiler is having with my code?
The compiler is referring to the value the match arm bodies return, not the type of the pattern of each match arm.
Some(count) => *count += 1,
None => map.insert(a, 0),
The expression *count += 1 evaluates to () (called "unit" in Rust, "void" in many other languages). The expression map.insert(a, 0) on the other hand returns Option<V> where V is the value type of the hash map (an integer in your case). Suddenly the error message does make some sense:
= note: expected type `()`
= note: found type `std::option::Option<{integer}>`
I suppose you don't even want to return something from the match block (remember: match blocks are expressions, too, so you could return something from it). To discard the result of any expression, you can convert it to a statement with ;. Let's try this:
match map.get(&a) {
Some(count) => {
*count += 1;
}
None => {
map.insert(a, 0);
}
}
Each match arm body is a block now (something between { and }) and each block contains one statement. Note that we technically don't need to change the first match arm, as *count += 1 already returns (), but this way it's more consistent.
But once you test this, another error related to borrowing will be shown. This is a well known issue and is explained in more detail here. In short: the borrow checker is not smart enough to recognize that your code is fine and therefore you should use the super nice entry-API:
let map = HashMap::new();
map.insert("a", 0);
let a = "a";
*map.entry(&a).or_insert(0) += 1;

Resources