This question already has an answer here:
How can I store a pattern in a variable in Rust?
(1 answer)
Closed 3 years ago.
This works:
match 0 {
0..=9 => (),
_ => (),
}
But this doesn't:
const R: std::ops::RangeInclusive<u8> = 0..=9;
fn main() {
match 0 {
R => (),
_ => (),
}
}
playground.
If I want to use a range with match, must I use a literal range?
I thinnk the problem is that when you match an u8, each match arm must offer values of type u8 that can be compared to the parameter.
Now, if you write 0..=9, this is not a RangeInclusive (even if it looks the same) - it is a range pattern that can be used to describe a range of values in a match arm.
So, if you write R => () in a match arm, the compiler complains (imo rightfully):
error[E0308]: mismatched types
--> src/main.rs:7:9
|
7 | R => (),
| ^ expected integer, found struct `std::ops::RangeInclusive`
|
= note: expected type `u8`
found type `std::ops::RangeInclusive<u8>`
I.e. it expects a u8 (or, implicitly, a range pattern for u8 values), but it found a RangeInclusive.
Now, one possible solution could be to define the lower and upper bound as separate constants:
const LOWER : u8 = 0;
const UPPER : u8 = 9;
const R: std::ops::RangeInclusive<u8> = LOWER..=UPPER; // <- this is a RangeInclusive<u8>
fn main() {
match 0 {
LOWER..=UPPER => (), // <- this is a range pattern
_ => (),
}
}
Another, in my opinion less appealing, would be a macro that just expands to your range:
macro_rules! myrng{() => {0..=9}}
const R: std::ops::RangeInclusive<u8> = myrng!();
fn main() {
match 0 {
myrng!() => (),
_ => (),
}
}
Related
I am trying to implement a "transform" function that takes a non-copy enum value and and modifies it based on a parameter, but some of the parameters do nothing. A simplified example is:
enum NonCopy {
A,
B,
C
}
fn transform(to_transfrom: &mut NonCopy, param: u32) -> () {
*to_transfrom = match param {
// transformations
1 => NonCopy::A,
2 => NonCopy::B,
// retains the value
_ => *to_transfrom
};
}
I know that since the NonCopy doesn't implement the Copy trait the value inside to_transform cannot be moved out, however if param is neither 1 or 2 the value of *to_transform is assigned to itself, so it stays the same and nothing should be moved, but the compiler doesn't recognize that.
How can I achieve such a pattern with an assignment to a match expression?
I know I could instead assign in the match expression, but the non-example version is bigger and I do not want to repeat so much code plus it is quite ugly.
A neat little trick in Rust is that when you break out of an expression (via return, break, etc.), you don't actually need to provide a value for that expression. In this case, you can return from the match arm without providing a value:
enum NonCopy {
A,
B,
C
}
fn transform(to_transfrom: &mut NonCopy, param: u32) -> () {
*to_transfrom = match param {
// transformations
1 => NonCopy::A,
2 => NonCopy::B,
// retains the value
_ => return,
};
}
I wanted to implement a function computing the number of digits within any generic type of integer. Here is the code I came up with:
extern crate num;
use num::Integer;
fn int_length<T: Integer>(mut x: T) -> u8 {
if x == 0 {
return 1;
}
let mut length = 0u8;
if x < 0 {
length += 1;
x = -x;
}
while x > 0 {
x /= 10;
length += 1;
}
length
}
fn main() {
println!("{}", int_length(45));
println!("{}", int_length(-45));
}
And here is the compiler output
error[E0308]: mismatched types
--> src/main.rs:5:13
|
5 | if x == 0 {
| ^ expected type parameter, found integral variable
|
= note: expected type `T`
found type `{integer}`
error[E0308]: mismatched types
--> src/main.rs:10:12
|
10 | if x < 0 {
| ^ expected type parameter, found integral variable
|
= note: expected type `T`
found type `{integer}`
error: cannot apply unary operator `-` to type `T`
--> src/main.rs:12:13
|
12 | x = -x;
| ^^
error[E0308]: mismatched types
--> src/main.rs:15:15
|
15 | while x > 0 {
| ^ expected type parameter, found integral variable
|
= note: expected type `T`
found type `{integer}`
error[E0368]: binary assignment operation `/=` cannot be applied to type `T`
--> src/main.rs:16:9
|
16 | x /= 10;
| ^ cannot use `/=` on type `T`
I understand that the problem comes from my use of constants within the function, but I don't understand why the trait specification as Integer doesn't solve this.
The documentation for Integer says it implements the PartialOrd, etc. traits with Self (which I assume refers to Integer). By using integer constants which also implement the Integer trait, aren't the operations defined, and shouldn't the compiler compile without errors?
I tried suffixing my constants with i32, but the error message is the same, replacing _ with i32.
Many things are going wrong here:
As Shepmaster says, 0 and 1 cannot be converted to everything implementing Integer. Use Zero::zero and One::one instead.
10 can definitely not be converted to anything implementing Integer, you need to use NumCast for that
a /= b is not sugar for a = a / b but an separate trait that Integer does not require.
-x is an unary operation which is not part of Integer but requires the Neg trait (since it only makes sense for signed types).
Here's an implementation. Note that you need a bound on Neg, to make sure that it results in the same type as T
extern crate num;
use num::{Integer, NumCast};
use std::ops::Neg;
fn int_length<T>(mut x: T) -> u8
where
T: Integer + Neg<Output = T> + NumCast,
{
if x == T::zero() {
return 1;
}
let mut length = 0;
if x < T::zero() {
length += 1;
x = -x;
}
while x > T::zero() {
x = x / NumCast::from(10).unwrap();
length += 1;
}
length
}
fn main() {
println!("{}", int_length(45));
println!("{}", int_length(-45));
}
The problem is that the Integer trait can be implemented by anything. For example, you could choose to implement it on your own struct! There wouldn't be a way to convert the literal 0 or 1 to your struct. I'm too lazy to show an example of implementing it, because there's 10 or so methods. ^_^
num::Zero and num::One
This is why Zero::zero and One::one exist. You can (very annoyingly) create all the other constants from repeated calls to those.
use num::{One, Zero}; // 0.4.0
fn three<T>() -> T
where
T: Zero + One,
{
let mut three = Zero::zero();
for _ in 0..3 {
three = three + One::one();
}
three
}
From and Into
You can also use the From and Into traits to convert to your generic type:
use num::Integer; // 0.4.0
use std::ops::{DivAssign, Neg};
fn int_length<T>(mut x: T) -> u8
where
T: Integer + Neg<Output = T> + DivAssign,
u8: Into<T>,
{
let zero = 0.into();
if x == zero {
return 1;
}
let mut length = 0u8;
if x < zero {
length += 1;
x = -x;
}
while x > zero {
x /= 10.into();
length += 1;
}
length
}
fn main() {
println!("{}", int_length(45));
println!("{}", int_length(-45));
}
See also:
How do I use floating point number literals when using generic types?
I'm trying to implement a function that reads command line arguments and compares them to hard-coded string literals.
When I do the comparison with an if statement it works like a charm:
fn main() {
let s = String::from("holla!");
if s == "holla!" {
println!("it worked!");
}
}
But using a match statement (which I guess would be more elegant):
fn main() {
let s = String::from("holla!");
match s {
"holla!" => println!("it worked!"),
_ => println!("nothing"),
}
}
I keep getting an error from the compiler that a String was expected but a &static str was found:
error[E0308]: mismatched types
--> src/main.rs:5:9
|
5 | "holla!" => println!("it worked!"),
| ^^^^^^^^ expected struct `std::string::String`, found reference
|
= note: expected type `std::string::String`
found type `&'static str`
I've seen How to match a String against string literals in Rust? so I know how to fix it, but I want to know why the comparison works when if but not using match.
I want to know why the comparison works when if but not using match.
It's not so much about if and more because you've used == in the condition. The condition in an if statement is any expression of type bool; you just happen to have chosen to use == there.
The == operator is really a function associated with the PartialEq trait. This trait can be implemented for any pair of types. And, for convenience, String has implementations for PartialEq<str> and PartialEq<&str>, among others - and vice versa.
On the other hand, match expressions use pattern matching for comparison, not ==. A &'static str literal, like "holla!", is a valid pattern, but it can never match a String, which is a completely different type.
Pattern matching lets you concisely compare parts of complex structures, even if the whole thing isn't equal, as well as bind variables to pieces of the match. While Strings don't really benefit from that, it's very powerful for other types, and has an entirely different purpose than ==.
Note that you can use pattern matching with if by instead using the if let construct. Your example would look like this:
if let "holla!" = &*s {
println!("it worked!");
}
Conversely, one way to use == inside a match is like this:
match s {
_ if s == "holla!" => println!("it worked!"),
_ => println!("nothing"),
}
Or, as #ljedrz suggested:
match s == "holla!" {
true => println!("it worked!"),
_ => println!("nothing")
}
As #peter-hall said, there's a mismatch of types because match expressions use pattern matching, which is different from the == that are associated with the PartialEq trait.
There a second way to resolve this issue, by casting your String into an &str (a string slice) :
match &s[..] {
"holla!" => println!("it worked!"),
"Hallo!" => println!("with easy to read matches !"),
_ => println!("nothing"),
}
I have a HashMap and need to get the first element:
type VarIdx = std::collections::HashMap<u16, u8>;
fn get_first_elem(idx: VarIdx) -> u16 {
let it = idx.iter();
let ret = match it.next() {
Some(x) => x,
None => -1,
};
ret
}
fn main() {}
but the code doesn't compile:
error[E0308]: match arms have incompatible types
--> src/main.rs:5:15
|
5 | let ret = match it.next() {
| _______________^
6 | | Some(x) => x,
7 | | None => -1,
8 | | };
| |_____^ expected tuple, found integral variable
|
= note: expected type `(&u16, &u8)`
found type `{integer}`
note: match arm with an incompatible type
--> src/main.rs:7:17
|
7 | None => -1,
| ^^
how can I fix it?
There is no such thing as the "first" item in a HashMap. There are no guarantees about the order in which the values are stored nor the order in which you will iterate over them.
If order is important then perhaps you can switch to a BTreeMap, which preserves order based on the keys.
If you just need to get the first value that you come across, in other words any value, you can do something similar to your original code: create an iterator, just taking the first value:
fn get_first_elem(idx: VarIdx) -> i16 {
match idx.values().next() {
Some(&x) => x as i16,
None => -1,
}
}
The method values() creates an iterator over just the values. The reason for your error is that iter() will create an iterator over pairs of keys and values which is why you got the error "expected tuple".
To make it compile, I had to change a couple of other things: -1 is not a valid u16 value so that had to become i16, and your values are u8 so had to be cast to i16.
As another general commentary, returning -1 to indicate failure is not very "Rusty". This is what Option is for and, given that next() already returns an Option, this is very easy to accomplish:
fn get_first_elem(idx: VarIdx) -> Option<u8> {
idx.values().copied().next()
}
The .copied() is needed in order to convert the &u8 values of the iterator into u8.
HashMap::iter returns an iterator over (&Key, &Value) pairs. What you want is HashMap::values, which produces an iterator that only produces the values of the HashMap.
Note that the order of the values is random. It has nothing to do with the order you put the values in or with the actual value of the values.
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.